ECDH 共享密钥在 Crypto++ 和 Android 之间不匹配
ECDH Shared Secret does not match between Crypto++ and Android
所以我正在使用 Java 和 Crypto++ 5.6.3 库在 Android 上编写 ECDH 实现。
我写了一些 C++ JNI 代码来调用 Crypto++ 函数,我有一个函数可以生成 public/private 密钥对,另一个函数可以提取共享密钥。但是似乎存在共享机密不匹配的问题。
情况如下。 Alice 和 Bob 都生成自己的 Public 和私钥对。他们成功地交换了 public 个密钥。
要获取共享秘密,Alice 会执行以下操作:
byte[] sharedSecret = getSharedSecret(bobPublicKey, alicePrivateKey);
Bob 做了类似的操作:
byte[] sharedSecret = getSharedSecret(alicePublicKey, bobPrivateKey);
我看到的问题是,两个共享机密彼此不匹配。我对这应该如何工作有什么误解吗?
我假设我这边只有一个与共享机密相关的具体实施问题,但我不确定。 C++ JNI 实现如下。 retrieveSharedSecret 函数总是输出 "It Worked"。关于我在这里做错了什么有什么想法吗?
JNIEXPORT jobject JNICALL Java_com_myproject_test_cryptopp_ECDHLibrary_generateKeyPair
(JNIEnv *env, jclass)
{
// Generate a public private key pair using ECDH (Elliptic Curve Diffie Hellman)
OID CURVE = secp256r1(); // the key is 256 bits (32 bytes) long
AutoSeededRandomPool rng;
// Because we are using point compression
// Private Key 32 bytes
// Public Key 33 bytes
// If compression was not used the public key would be 65 bytes long
ECDH < ECP >::Domain dhA( CURVE );
dhA.AccessGroupParameters().SetPointCompression(true);
SecByteBlock privA(dhA.PrivateKeyLength()), pubA(dhA.PublicKeyLength());
dhA.GenerateKeyPair(rng, privA, pubA);
jobject publicKeyByteBuffer = (*env).NewDirectByteBuffer(pubA.BytePtr(), pubA.SizeInBytes());
jobject privateKeyByteBuffer = (*env).NewDirectByteBuffer(privA.BytePtr(), privA.SizeInBytes());
// Return the ECDH Key Pair back as our custom Java ECDHKeyPair class object
jclass keyPairClass = (*env).FindClass("com/myproject/test/cryptopp/ECDHKeyPair");
jmethodID midConstructor = (*env).GetMethodID(keyPairClass, "<init>", "(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)V");
jobject keyPairObject = (*env).NewObject(keyPairClass, midConstructor, publicKeyByteBuffer, privateKeyByteBuffer);
return keyPairObject;
}
JNIEXPORT jobject JNICALL Java_com_myproject_test_cryptopp_ECDHLibrary_retrieveSharedSecret
(JNIEnv *env, jclass, jbyteArray publicKeyArray, jbyteArray privateKeyArray)
{
// Use the same ECDH Setup that is specified in the generateKeyPair method above
OID CURVE = secp256r1();
DL_GroupParameters_EC<ECP> params(CURVE);
ECDH<ECP>::Domain dhAgreement(params);
dhAgreement.AccessGroupParameters().SetPointCompression(true);
// Figure out how big the public and private keys are
// Public Key: This belongs to the other user
// Private Key: This is out personal private key
int pubLen = (int)(*env).GetArrayLength(publicKeyArray);
int privLen = (int)(*env).GetArrayLength(privateKeyArray);
// Convert the keys from a jbyteArray to a SecByteBlock so that they can be passed
// into the CryptoPP Library functions.
unsigned char* pubData = new unsigned char[pubLen];
(*env).GetByteArrayRegion(publicKeyArray, 0, pubLen, reinterpret_cast<jbyte*>(pubData));
unsigned char* privData = new unsigned char[privLen];
(*env).GetByteArrayRegion(privateKeyArray, 0, privLen, reinterpret_cast<jbyte*>(privData));
SecByteBlock pubB(pubData, pubLen) , privA(privData, privLen);
// Now extract shared secret between the two keys
SecByteBlock sharedSecretByteBlock(dhAgreement.AgreedValueLength());
ALOG("Shared Agreed Value Length: %d", dhAgreement.AgreedValueLength());
bool didWork = dhAgreement.Agree(sharedSecretByteBlock, privA, pubB);
ALOG("Key Agreement: %s", didWork ? "It Worked" : "It Failed");
ALOG("Shared Secret Byte Size: %d", sharedSecretByteBlock.SizeInBytes());
// Return the shared secret as a Java ByteBuffer
jobject publicKeyByteBuffer = (*env).NewDirectByteBuffer(sharedSecretByteBlock.BytePtr(), sharedSecretByteBlock.SizeInBytes());
return publicKeyByteBuffer;
}
编辑:
我把我的测试项目放在Github here上,这样其他人可以看看并试试自己的运气。在 README 中包含一些关于如何启动它的说明和 运行.
我在朋友的帮助下弄明白了。问题是 retrieveSharedSecret 方法以及它直接返回一个字节缓冲区的事实,它指向一个内存地址,该地址在 C++ 方法调用期间在范围内,但一旦返回到 Java代码。所以我基本上是把垃圾内存作为我的共享秘密。
我调整了代码,使方法 returns 成为自定义 SharedSecret Java 对象,就像 keyGeneration 方法一样。这样做可以让我正确复制我需要的所有信息,而不必担心这个范围问题。
修改后的方法代码如下。我还将更新 Github 项目,以便它可以作为一个工作示例存在,说明如何将 Android Studio 与 NDK (non-experimental) 和 CryptoPP.
一起使用
// Use the same ECDH Setup that is specified in the generateKeyPair method above
OID CURVE = secp256r1();
DL_GroupParameters_EC<ECP> params(CURVE);
ECDH<ECP>::Domain dhAgreement(params);
dhAgreement.AccessGroupParameters().SetPointCompression(true);
// Figure out how big the public and private keys are
// Public Key: This belongs to the other user
// Private Key: This is out personal private key
int pubLen = (int)(*env).GetArrayLength(publicKeyArray);
int privLen = (int)(*env).GetArrayLength(privateKeyArray);
// Convert the keys from a jbyteArray to a SecByteBlock so that they can be passed
// into the CryptoPP Library functions.
unsigned char* pubData = new unsigned char[pubLen];
(*env).GetByteArrayRegion(publicKeyArray, 0, pubLen, reinterpret_cast<jbyte*>(pubData));
unsigned char* privData = new unsigned char[privLen];
(*env).GetByteArrayRegion(privateKeyArray, 0, privLen, reinterpret_cast<jbyte*>(privData));
SecByteBlock pubB(pubData, pubLen) , privA(privData, privLen);
// Now extract shared secret between the two keys
SecByteBlock sharedSecretByteBlock(dhAgreement.AgreedValueLength());
ALOG("Shared Agreed Value Length: %d", dhAgreement.AgreedValueLength());
bool didWork = dhAgreement.Agree(sharedSecretByteBlock, privA, pubB);
ALOG("Key Agreement: %s", didWork ? "It Worked" : "It Failed");
ALOG("Shared Secret Byte Size: %d", sharedSecretByteBlock.SizeInBytes());
// Return the shared secret as a Java ByteBuffer
jobject sharedSecretByteBuffer = (*env).NewDirectByteBuffer(sharedSecretByteBlock.BytePtr(), sharedSecretByteBlock.SizeInBytes());
// Return the ECDH Key Pair back as a Java ECDHKeyPair object
jclass keyPairClass = (*env).FindClass("com/tcolligan/ecdhtest/SharedSecret");
jmethodID midConstructor = (*env).GetMethodID(keyPairClass, "<init>", "(Ljava/nio/ByteBuffer;)V");
jobject sharedSecretObject = (*env).NewObject(keyPairClass, midConstructor, sharedSecretByteBuffer);
return sharedSecretObject;
所以我正在使用 Java 和 Crypto++ 5.6.3 库在 Android 上编写 ECDH 实现。
我写了一些 C++ JNI 代码来调用 Crypto++ 函数,我有一个函数可以生成 public/private 密钥对,另一个函数可以提取共享密钥。但是似乎存在共享机密不匹配的问题。
情况如下。 Alice 和 Bob 都生成自己的 Public 和私钥对。他们成功地交换了 public 个密钥。
要获取共享秘密,Alice 会执行以下操作:
byte[] sharedSecret = getSharedSecret(bobPublicKey, alicePrivateKey);
Bob 做了类似的操作:
byte[] sharedSecret = getSharedSecret(alicePublicKey, bobPrivateKey);
我看到的问题是,两个共享机密彼此不匹配。我对这应该如何工作有什么误解吗?
我假设我这边只有一个与共享机密相关的具体实施问题,但我不确定。 C++ JNI 实现如下。 retrieveSharedSecret 函数总是输出 "It Worked"。关于我在这里做错了什么有什么想法吗?
JNIEXPORT jobject JNICALL Java_com_myproject_test_cryptopp_ECDHLibrary_generateKeyPair
(JNIEnv *env, jclass)
{
// Generate a public private key pair using ECDH (Elliptic Curve Diffie Hellman)
OID CURVE = secp256r1(); // the key is 256 bits (32 bytes) long
AutoSeededRandomPool rng;
// Because we are using point compression
// Private Key 32 bytes
// Public Key 33 bytes
// If compression was not used the public key would be 65 bytes long
ECDH < ECP >::Domain dhA( CURVE );
dhA.AccessGroupParameters().SetPointCompression(true);
SecByteBlock privA(dhA.PrivateKeyLength()), pubA(dhA.PublicKeyLength());
dhA.GenerateKeyPair(rng, privA, pubA);
jobject publicKeyByteBuffer = (*env).NewDirectByteBuffer(pubA.BytePtr(), pubA.SizeInBytes());
jobject privateKeyByteBuffer = (*env).NewDirectByteBuffer(privA.BytePtr(), privA.SizeInBytes());
// Return the ECDH Key Pair back as our custom Java ECDHKeyPair class object
jclass keyPairClass = (*env).FindClass("com/myproject/test/cryptopp/ECDHKeyPair");
jmethodID midConstructor = (*env).GetMethodID(keyPairClass, "<init>", "(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)V");
jobject keyPairObject = (*env).NewObject(keyPairClass, midConstructor, publicKeyByteBuffer, privateKeyByteBuffer);
return keyPairObject;
}
JNIEXPORT jobject JNICALL Java_com_myproject_test_cryptopp_ECDHLibrary_retrieveSharedSecret
(JNIEnv *env, jclass, jbyteArray publicKeyArray, jbyteArray privateKeyArray)
{
// Use the same ECDH Setup that is specified in the generateKeyPair method above
OID CURVE = secp256r1();
DL_GroupParameters_EC<ECP> params(CURVE);
ECDH<ECP>::Domain dhAgreement(params);
dhAgreement.AccessGroupParameters().SetPointCompression(true);
// Figure out how big the public and private keys are
// Public Key: This belongs to the other user
// Private Key: This is out personal private key
int pubLen = (int)(*env).GetArrayLength(publicKeyArray);
int privLen = (int)(*env).GetArrayLength(privateKeyArray);
// Convert the keys from a jbyteArray to a SecByteBlock so that they can be passed
// into the CryptoPP Library functions.
unsigned char* pubData = new unsigned char[pubLen];
(*env).GetByteArrayRegion(publicKeyArray, 0, pubLen, reinterpret_cast<jbyte*>(pubData));
unsigned char* privData = new unsigned char[privLen];
(*env).GetByteArrayRegion(privateKeyArray, 0, privLen, reinterpret_cast<jbyte*>(privData));
SecByteBlock pubB(pubData, pubLen) , privA(privData, privLen);
// Now extract shared secret between the two keys
SecByteBlock sharedSecretByteBlock(dhAgreement.AgreedValueLength());
ALOG("Shared Agreed Value Length: %d", dhAgreement.AgreedValueLength());
bool didWork = dhAgreement.Agree(sharedSecretByteBlock, privA, pubB);
ALOG("Key Agreement: %s", didWork ? "It Worked" : "It Failed");
ALOG("Shared Secret Byte Size: %d", sharedSecretByteBlock.SizeInBytes());
// Return the shared secret as a Java ByteBuffer
jobject publicKeyByteBuffer = (*env).NewDirectByteBuffer(sharedSecretByteBlock.BytePtr(), sharedSecretByteBlock.SizeInBytes());
return publicKeyByteBuffer;
}
编辑: 我把我的测试项目放在Github here上,这样其他人可以看看并试试自己的运气。在 README 中包含一些关于如何启动它的说明和 运行.
我在朋友的帮助下弄明白了。问题是 retrieveSharedSecret 方法以及它直接返回一个字节缓冲区的事实,它指向一个内存地址,该地址在 C++ 方法调用期间在范围内,但一旦返回到 Java代码。所以我基本上是把垃圾内存作为我的共享秘密。
我调整了代码,使方法 returns 成为自定义 SharedSecret Java 对象,就像 keyGeneration 方法一样。这样做可以让我正确复制我需要的所有信息,而不必担心这个范围问题。
修改后的方法代码如下。我还将更新 Github 项目,以便它可以作为一个工作示例存在,说明如何将 Android Studio 与 NDK (non-experimental) 和 CryptoPP.
一起使用// Use the same ECDH Setup that is specified in the generateKeyPair method above
OID CURVE = secp256r1();
DL_GroupParameters_EC<ECP> params(CURVE);
ECDH<ECP>::Domain dhAgreement(params);
dhAgreement.AccessGroupParameters().SetPointCompression(true);
// Figure out how big the public and private keys are
// Public Key: This belongs to the other user
// Private Key: This is out personal private key
int pubLen = (int)(*env).GetArrayLength(publicKeyArray);
int privLen = (int)(*env).GetArrayLength(privateKeyArray);
// Convert the keys from a jbyteArray to a SecByteBlock so that they can be passed
// into the CryptoPP Library functions.
unsigned char* pubData = new unsigned char[pubLen];
(*env).GetByteArrayRegion(publicKeyArray, 0, pubLen, reinterpret_cast<jbyte*>(pubData));
unsigned char* privData = new unsigned char[privLen];
(*env).GetByteArrayRegion(privateKeyArray, 0, privLen, reinterpret_cast<jbyte*>(privData));
SecByteBlock pubB(pubData, pubLen) , privA(privData, privLen);
// Now extract shared secret between the two keys
SecByteBlock sharedSecretByteBlock(dhAgreement.AgreedValueLength());
ALOG("Shared Agreed Value Length: %d", dhAgreement.AgreedValueLength());
bool didWork = dhAgreement.Agree(sharedSecretByteBlock, privA, pubB);
ALOG("Key Agreement: %s", didWork ? "It Worked" : "It Failed");
ALOG("Shared Secret Byte Size: %d", sharedSecretByteBlock.SizeInBytes());
// Return the shared secret as a Java ByteBuffer
jobject sharedSecretByteBuffer = (*env).NewDirectByteBuffer(sharedSecretByteBlock.BytePtr(), sharedSecretByteBlock.SizeInBytes());
// Return the ECDH Key Pair back as a Java ECDHKeyPair object
jclass keyPairClass = (*env).FindClass("com/tcolligan/ecdhtest/SharedSecret");
jmethodID midConstructor = (*env).GetMethodID(keyPairClass, "<init>", "(Ljava/nio/ByteBuffer;)V");
jobject sharedSecretObject = (*env).NewObject(keyPairClass, midConstructor, sharedSecretByteBuffer);
return sharedSecretObject;