简体   繁体   中英

iOS - swift - Generating key pair for secp224k1 curve (ECDH)

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.

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

  • select root node in project navigator
  • in Xcode create 'New Group'
  • name it 'Frameworks'
  • 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

  • select the VSCCrypto.framework

Objective-C++ Wrapper

  • create a ECDHCrypto Objective-C file
  • when asked for 'Would you like to configure an Objective-C bridging header' tap on 'Create Bridging Header'
  • in the project navigator change the file suffix from.m to.mm (to allow C++ code there)
  • add #import "ECDHCrypto.h" to the bridging header

ECDHCrypto.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:

  1. In Java, a secp224k1 key pair is generated and the public key is output to the console.

  2. 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).

  3. 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:

相同的共享秘密

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM