iOS - swift - 为 secp224k1 曲线生成密钥对 (ECDH)
iOS - swift - Generating key pair for secp224k1 curve (ECDH)
我一直在尝试为 iOS 中的 secp224k1 曲线生成 public 和私钥。我们正在使用 ECDH 方法在移动端和后端之间进行 api 握手。在 Java 中,使用以下代码完成。
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();
}
有没有办法用 swift 中的特定曲线 (secp224k1) 类型生成密钥?我尝试使用苹果提供的 EC 算法通过以下代码进行握手。
//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]
}
当我发送通过上述方法生成的 public 密钥时,我从服务器收到一条错误消息:
"error":"java.security.InvalidKeyException: ECDH key agreement requires ECPublicKey for doPhase","exception":"InvalidAuthException"
我为 swift 尝试了 VirgilCrypto,这接近于解决问题。但是它在库中没有我需要的特定曲线类型。它仅支持 secp256r1。另外,我尝试了以下帖子的答案,但没有成功。
Elliptic Curve Diffie Hellman in ios/swift
任何建议或帮助都将非常有用,谢谢。
iOS 不支持 Koblitz 224 位曲线。一种解决方案可能是使用不同的曲线类型或支持 secp224k1 的第三方库。
从您的评论可以得出结论,secp224k1 曲线类型是必需的。
可能使用的第三方库是 Virgil Crypto,可通过 github https://github.com/VirgilSecurity/virgil-crypto 获得。它是一个 C++ 库。 (Virgil Security 还提供了一个名为 virgil-crypto-x 的 Swift 包装器库,但该库在当前版本中不再支持 secp224k1)。
C++ 库可以在 Swift 中间接使用,方法是创建具有已定义接口的 Objective-C++ 包装器。
建造VSCCrypto.framework
在命令行输入:
git clone https://github.com/VirgilSecurity/virgil-crypto
cd virgil-crypto
utils/build.sh --target=ios
这为 iOS 构建了框架。
将 VSCCrypto.framework 添加到 Xcode 项目
- select 项目导航器中的根节点
- 在 Xcode 中创建 'New Group'
- 命名为'Frameworks'
在 Finder 中将文件夹 virgil-crypto/build/ios/lib 中的 VSCCrypto.framework 拖放到 'Frameworks' 组
在XCode右边'Embedded Binaries'下点击加
- select VSCCrypto.framework
Objective-C++包装器
- 创建 ECDHCrypto Objective-C 文件
- 当询问 [=109=] 时,点击 'Create Bridging Header'
- 在项目导航器中将文件后缀从 .m 更改为 .mm(以允许 C++ 代码存在)
- 将
#import "ECDHCrypto.h"
添加到桥接头
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
在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));
与 Java 对手进行测试
要测试密钥交换是否成功,可以进行如下测试:
在Java中生成secp224k1密钥对,并将public密钥输出到控制台。
public 密钥被复制到使用 Copy/Paste 的 iOS 应用程序的 Swift 代码中。然后,该应用程序生成一个密钥对并将其自己的 public 密钥以及计算出的共享密钥写入控制台。然后 iOS public 键作为输入插入到 Java 程序中(显示为绿色)。
最后,可以比较一下iOS应用程序和Java程序的共享秘密。这里也是一样,所以密钥交换成功。
在上部区域您可以看到 Xcode 和 iOS 源代码,在下部区域可以看到 Java 程序的输出:
我一直在尝试为 iOS 中的 secp224k1 曲线生成 public 和私钥。我们正在使用 ECDH 方法在移动端和后端之间进行 api 握手。在 Java 中,使用以下代码完成。
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();
}
有没有办法用 swift 中的特定曲线 (secp224k1) 类型生成密钥?我尝试使用苹果提供的 EC 算法通过以下代码进行握手。
//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]
}
当我发送通过上述方法生成的 public 密钥时,我从服务器收到一条错误消息:
"error":"java.security.InvalidKeyException: ECDH key agreement requires ECPublicKey for doPhase","exception":"InvalidAuthException"
我为 swift 尝试了 VirgilCrypto,这接近于解决问题。但是它在库中没有我需要的特定曲线类型。它仅支持 secp256r1。另外,我尝试了以下帖子的答案,但没有成功。
Elliptic Curve Diffie Hellman in ios/swift
任何建议或帮助都将非常有用,谢谢。
iOS 不支持 Koblitz 224 位曲线。一种解决方案可能是使用不同的曲线类型或支持 secp224k1 的第三方库。
从您的评论可以得出结论,secp224k1 曲线类型是必需的。
可能使用的第三方库是 Virgil Crypto,可通过 github https://github.com/VirgilSecurity/virgil-crypto 获得。它是一个 C++ 库。 (Virgil Security 还提供了一个名为 virgil-crypto-x 的 Swift 包装器库,但该库在当前版本中不再支持 secp224k1)。
C++ 库可以在 Swift 中间接使用,方法是创建具有已定义接口的 Objective-C++ 包装器。
建造VSCCrypto.framework
在命令行输入:
git clone https://github.com/VirgilSecurity/virgil-crypto
cd virgil-crypto
utils/build.sh --target=ios
这为 iOS 构建了框架。
将 VSCCrypto.framework 添加到 Xcode 项目
- select 项目导航器中的根节点
- 在 Xcode 中创建 'New Group'
- 命名为'Frameworks'
在 Finder 中将文件夹 virgil-crypto/build/ios/lib 中的 VSCCrypto.framework 拖放到 'Frameworks' 组
在XCode右边'Embedded Binaries'下点击加
- select VSCCrypto.framework
Objective-C++包装器
- 创建 ECDHCrypto Objective-C 文件
- 当询问 [=109=] 时,点击 'Create Bridging Header'
- 在项目导航器中将文件后缀从 .m 更改为 .mm(以允许 C++ 代码存在)
- 将
#import "ECDHCrypto.h"
添加到桥接头
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
在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));
与 Java 对手进行测试
要测试密钥交换是否成功,可以进行如下测试:
在Java中生成secp224k1密钥对,并将public密钥输出到控制台。
public 密钥被复制到使用 Copy/Paste 的 iOS 应用程序的 Swift 代码中。然后,该应用程序生成一个密钥对并将其自己的 public 密钥以及计算出的共享密钥写入控制台。然后 iOS public 键作为输入插入到 Java 程序中(显示为绿色)。
最后,可以比较一下iOS应用程序和Java程序的共享秘密。这里也是一样,所以密钥交换成功。
在上部区域您可以看到 Xcode 和 iOS 源代码,在下部区域可以看到 Java 程序的输出: