Reputation: 945
I have been trying to generate public and private keys for the secp224k1 curve in iOS. We are using ECDH method to do api handshake between mobile and backend. In Java it is done using the below code.
public static KeyPair getECKeyPair() throws NoSuchProviderException, NoSuchAlgorithmException, InvalidAlgorithmParameterException {
ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp224k1");
KeyPairGenerator kpg = KeyPairGenerator.getInstance("ECDH", "SC");
kpg.initialize(ecSpec);
return kpg.generateKeyPair();
}
Is there a way to generate keys with the specific curve(secp224k1) type in swift? I tried using the apple provided EC algorithm to do the handshake by using the below code.
//Generates public and private key with EC algorithm
public static func getKey() -> [String: SecKey]? {
let attributes: [String: Any] =
[kSecAttrKeySizeInBits as String: 256,
kSecAttrKeyType as String: kSecAttrKeyTypeEC,
kSecPrivateKeyAttrs as String:
[kSecAttrIsPermanent as String: false]
]
var error: Unmanaged<CFError>?
guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
let err = error!.takeRetainedValue() as Error
print(err.localizedDescription)
return nil
}
guard let publicKey = SecKeyCopyPublicKey(privateKey) else {
print("Error occured while creating public key")
return nil
}
return ["publicKey": publicKey, "privateKey": privateKey]
}
When I send the public key generated via above method I get an error from server with a message:
"error":"java.security.InvalidKeyException: ECDH key agreement requires ECPublicKey for doPhase","exception":"InvalidAuthException"
I tried VirgilCrypto for swift which came close to solving the problem. But it doesn't have the specific curve type in the library which I need. It has support for secp256r1 only. Also, answers from the below posts I tried and didn't work out.
Elliptic Curve Diffie Hellman in ios/swift
Any suggestions or help would be great, Thanks.
Upvotes: 3
Views: 2606
Reputation: 27096
The Koblitz 224-bit curve is not supported by iOS. One solution might be to use a different curve type or a third-party library with secp224k1 support.
From your comments it can be concluded that the secp224k1 curve type is a requirement.
A third-party library that might be used is Virgil Crypto, which is available through github https://github.com/VirgilSecurity/virgil-crypto. It is a C++ library. (Virgil Security is also offering a Swift wrapper lib called virgil-crypto-x, but that one no longer supports secp224k1 in the current version).
C++ libs can be used in Swift indirectly by creating a Objective-C++ wrapper with a defined interface.
Build VSCCrypto.framework
On the command line enter:
git clone https://github.com/VirgilSecurity/virgil-crypto
cd virgil-crypto
utils/build.sh --target=ios
This builds the framework for iOS.
Add VSCCrypto.framework to Xcode Project
drag & drop in Finder the VSCCrypto.framework in the folder virgil-crypto/build/ios/lib to the 'Frameworks' group
in XCode on the right under 'Embedded Binaries' tap plus
Objective-C++ Wrapper
#import "ECDHCrypto.h"
to the bridging headerECDHCrypto.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface ECDHCrypto : NSObject
@property(nonatomic, strong) NSString *ownPrivateKey;
@property(nonatomic, strong) NSString *ownPublicKey;
- (void)generateKeyPair;
- (NSString *)shared:(NSString *)otherPublicKey;
@end
NS_ASSUME_NONNULL_END
ECDHCrypto.mm
#import "ECDHCrypto.h"
#import <VSCCrypto/VirgilCrypto.h>
using virgil::crypto::VirgilKeyPair;
using virgil::crypto::VirgilByteArray;
using virgil::crypto::VirgilCipherBase;
using virgil::crypto::str2bytes;
using virgil::crypto::bytes2str;
using virgil::crypto::bytes2hex;
@implementation ECDHCrypto
- (void)generateKeyPair {
VirgilKeyPair keyPair = VirgilKeyPair::generate(VirgilKeyPair::Type::EC_SECP224K1);
VirgilByteArray ownPublicKeyBates = keyPair.publicKey();
self.ownPublicKey = [NSString stringWithCString:bytes2str(ownPublicKeyBates).c_str()
encoding:[NSString defaultCStringEncoding]];
VirgilByteArray ownPrivateKeyBytes = keyPair.privateKey();
self.ownPrivateKey = [NSString stringWithCString:bytes2str(ownPrivateKeyBytes).c_str()
encoding:[NSString defaultCStringEncoding]];
}
- (NSString *)shared:(NSString *)otherPublicKey {
NSAssert(self.ownPrivateKey, @"private key must be set, e.g. use generateKeyPair");
std::string otherPKString([otherPublicKey cStringUsingEncoding:NSASCIIStringEncoding]);
VirgilByteArray pubKey = str2bytes(otherPKString);
std::string ownPrivateKeyString([self.ownPrivateKey cStringUsingEncoding:NSASCIIStringEncoding]);
VirgilByteArray ownPrivateKeyBytes = str2bytes(ownPrivateKeyString);
VirgilByteArray shared_ba = VirgilCipherBase::computeShared(pubKey, ownPrivateKeyBytes);
std::string hex = bytes2hex(shared_ba);
NSString *shared = [NSString stringWithCString:hex.c_str()
encoding:[NSString defaultCStringEncoding]];
return shared;
}
@end
Usage in Swift
let otherPK = """
-----BEGIN PUBLIC KEY-----
ME4wEAYHKoZIzj0CAQYFK4EEACADOgAEgeW/foqxCDOd1y6lnXONkRThS6xhjLHP
SEXs7jHSpoaPQH4vArcGmIb1cAZcepEh7WDQxCyfQXg=
-----END PUBLIC KEY-----
"""
let ecdhCrypto = ECDHCrypto()
ecdhCrypto.generateKeyPair();
print("ecdhCrypto.ownPublicKey: \n" + ecdhCrypto.ownPublicKey);
print("shared secret: " + ecdhCrypto.shared(otherPK));
Test With Java Counterpart
To test whether a key exchange is successful, one can perform the following test:
In Java, a secp224k1 key pair is generated and the public key is output to the console.
The public key is copied into the Swift code of an iOS application using Copy/Paste. The app then generates a key pair and writes its own public key to the console, as well as the calculated shared secret. The iOS public key is then inserted into the Java program as an input (displayed in green).
Finally, one can compare the shared secret of the iOS app and the Java program. Here it is the same, so the key exchange was successful.
In the upper area you see Xcode with the iOS source code and in the lower area the output of the Java program:
Upvotes: 5