ano*_*nim 6 openssl bouncycastle elliptic-curve ios
我正在尝试在iOS应用程序和java servlet之间实现AES加密.Java servlet使用BouncyCastle库,而iOS应用程序使用OpenSSL.虽然我已经为双方使用了相同的公钥/私钥对和域参数,但OpenSSL生成的共享密钥有时与服务器端BouncyCastle生成的密钥不同.
程序如下;
server_public_key
,server_private_key
)server_public_key
以EC_POINT
X和Y的形式嵌入iOS应用程序中client_key_curve
是一个EC_KEY
),和server_public_key
并计算共享的secret(key_agreement
)client_public_key
(从中提取client_key_curve
)以及使用派生的共享秘密(key_agreement
)对称加密的加密消息被发送到服务器client_public_key
和服务器ECDH参数计算共享密钥
,这些参数与客户端相同,并且key_agreement
但解密的消息并不总是与客户端发送的消息相同.
由于我还开发了一个使用相同程序但使用BouncyCastle进行加密的Android应用程序,因此我怀疑使用OpenSSL实现代码的正确性,因此这里显示的代码可供其他人帮助解决问题.我用来计算共享密钥的方法如下
- (void)calculateSharedSecret
{
BN_CTX* bn_ctx;
EC_KEY* client_key_curve = NULL;
EC_KEY* server_key_curve = NULL;
EC_GROUP* client_key_group = NULL;
EC_GROUP* server_key_group = NULL;
EC_POINT* client_publicKey = NULL;
EC_POINT* server_publicKey = NULL;
BIGNUM* client_privatKey = NULL;
BIGNUM* client_publicK_x = NULL;
BIGNUM* client_publicK_y = NULL;
BIGNUM* server_publicK_x = NULL;
BIGNUM* server_publicK_y = NULL;
NSException *p = [NSException exceptionWithName:@"" reason:@"" userInfo:nil];
bn_ctx = BN_CTX_new();
BN_CTX_start(bn_ctx);
client_publicK_x = BN_CTX_get(bn_ctx);
client_publicK_y = BN_CTX_get(bn_ctx);
client_privatKey = BN_CTX_get(bn_ctx);
server_publicK_x = BN_CTX_get(bn_ctx);
server_publicK_y = BN_CTX_get(bn_ctx);
// client
if ((client_key_curve = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) == NULL)
@throw p;
if ((client_key_group = (EC_GROUP *)EC_KEY_get0_group(client_key_curve)) == NULL)
@throw p;
if (EC_KEY_generate_key(client_key_curve) != 1)
@throw p;
if ((client_publicKey = (EC_POINT *)EC_KEY_get0_public_key(client_key_curve)) == NULL)
@throw p;
if (EC_KEY_check_key(client_key_curve) != 1)
@throw p;
client_privatKey = (BIGNUM *)EC_KEY_get0_private_key(client_key_curve);
char *client_public_key = EC_POINT_point2hex(client_key_group, client_publicKey, POINT_CONVERSION_COMPRESSED, bn_ctx);
char *client_privat_key = BN_bn2hex(client_privatKey);
_clientPublicKey = [NSString stringWithCString:client_public_key encoding:NSUTF8StringEncoding];
// server
NSArray* lines = [self loadServerPublicKeyXY];
NSString *public_str_x = [lines objectAtIndex:0];
NSString *public_str_y = [lines objectAtIndex:1];
BN_dec2bn(&server_publicK_x, [public_str_x UTF8String]);
BN_dec2bn(&server_publicK_y, [public_str_y UTF8String]);
if ((server_key_curve = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) == NULL)
@throw p;
if ((server_key_group = (EC_GROUP *)EC_KEY_get0_group(server_key_curve)) == NULL)
@throw p;
if (EC_KEY_generate_key(server_key_curve) != 1)
@throw p;
if ((server_publicKey = EC_POINT_new(server_key_group)) == NULL)
@throw p;
if (EC_POINT_set_affine_coordinates_GFp(server_key_group, server_publicKey, server_publicK_x, server_publicK_y, bn_ctx) != 1)
@throw p;
if (EC_KEY_check_key(server_key_curve) != 1)
@throw p;
unsigned char *key_agreement = NULL;
key_agreement = (unsigned char *)OPENSSL_malloc(SHA_DIGEST_LENGTH);
if (ECDH_compute_key(key_agreement, SHA_DIGEST_LENGTH, server_publicKey, client_key_curve, KDF1_SHA1) == 0)
@throw p;
_symmetricKey = [NSData dataWithBytes:key_agreement length:16];
}
Run Code Online (Sandbox Code Playgroud)
和
void *KDF1_SHA1(const void *input, size_t inlen, void *output, size_t *outlen)
{
if (*outlen < SHA_DIGEST_LENGTH)
return NULL;
else
*outlen = SHA_DIGEST_LENGTH;
return SHA1(input, inlen, output);
}
Run Code Online (Sandbox Code Playgroud)
_clientPublicKey
并_symmetricKey
在班级宣布
两侧使用相同的曲线(名为prime256v1或secp256r1),但结果并不总是相同.
编辑1:为响应@PeterDettman,我发布了服务器端代码以获得更多说明
public byte[] generateAESSymmetricKey(byte[] client_public_key_hex) throws InvalidRequest{
try {
// ECDH Private Key as well as other prime256v1 params was generated by Java "keytool" and stored in a JKS file
KeyStore keyStore = ...;
PrivateKey privateKey = (PrivateKey) keyStore.getKey("keyAlias", "keyStorePassword".toCharArray());
ECPrivateKeyParameters ecdhPrivateKeyParameters = (ECPrivateKeyParameters) (PrivateKeyFactory.createKey(privateKey.getEncoded()));
ECCurve ecCurve = ecdhPrivateKeyParameters.getParameters().getCurve();
ECDomainParameters ecDomainParameters = ecdhPrivateKeyParameters.getParameters();
ECPublicKeyParameters client_public_key = new ECPublicKeyParameters(ecCurve.decodePoint(client_public_key_hex), ecDomainParameters);
BasicAgreement agree = new ECDHBasicAgreement();
agree.init(ecdhPrivateKeyParameters);
byte[] keyAgreement = agree.calculateAgreement(client_public_key).toByteArray();
SHA1Digest sha1Digest = new SHA1Digest();
sha1Digest.update(keyAgreement, 0, keyAgreement.length);
byte hashKeyAgreement[] = new byte[sha1Digest.getDigestSize()];
sha1Digest.doFinal(hashKeyAgreement, 0);
byte[] server_calculatd_symmetric_key = new byte[16];
System.arraycopy(hashKeyAgreement, 0, server_calculatd_symmetric_key, 0, server_calculatd_symmetric_key.length);
return server_calculatd_symmetric_key;
} catch (Throwable ignored) {
return null;
}
}
Run Code Online (Sandbox Code Playgroud)
其中client_public_key_hex
是client_public_key
被转换为字节的阵列.预期的结果是始终server_calculatd_symmetric_key
等于symmetricKey
.但它们并不总是一样的.
编辑2:作为对@PeterDettman回答的反馈,我做了一些修改以反映他的建议,虽然不平等率降低,但在任何一方产生的关键协议(共享秘密)在所有情况下都不相同.
可以用以下数据再现不等式情况之一
那么实现的代码或过程中是否有任何错误?
谢谢
服务端代码有问题,ECDH协议值转字节的方式:
byte[] keyAgreement = agree.calculateAgreement(client_public_key).toByteArray();
Run Code Online (Sandbox Code Playgroud)
试试这个:
BigInteger agreementValue = agree.calculateAgreement(client_public_key);
byte[] keyAgreement = BigIntegers.asUnsignedByteArray(agree.getFieldSize(), agreementValue);
Run Code Online (Sandbox Code Playgroud)
这将确保固定大小的字节数组作为输出,这是将 EC 字段元素转换为八位字节字符串的要求(搜索“字段元素到八位字节字符串转换原语”了解更多详细信息)。
我建议您忽略 SHA1 密钥派生部分,直到您可以获得 Java keyAgreement 字节数组以与 KDF1_SHA1 函数的输入完全匹配。
归档时间: |
|
查看次数: |
3206 次 |
最近记录: |