所以我正在使用Java和Crypto ++ 5.6.3库在Android上编写ECDH实现。
我写了一些C ++ JNI代码来调用Crypto ++函数,我有一个函数来生成公钥/私钥对,另一个函数来提取共享密钥。然而,似乎存在一个问题,即共享机密不匹配。
情况如下。 Alice和Bob都生成了自己的公钥和私钥对。他们成功地交换了公钥。
为获取共享密钥,Alice执行以下操作:
byte[] sharedSecret = getSharedSecret(bobPublicKey, alicePrivateKey);
鲍勃做了类似的操作:
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中有关如何启动和运行的一些说明。
答案 0 :(得分:1)
我能够在朋友的帮助下解决这个问题。问题是retrieveSharedSecret方法以及它直接返回一个字节缓冲区的事实,该缓冲区指向C ++方法调用期间在范围内的内存地址,但是一旦它返回到Java代码中就会超出范围。所以我基本上把垃圾记忆作为我的共同秘密。
我调整了代码,以便该方法返回一个自定义的SharedSecret Java对象,就像keyGeneration方法一样。这样做可以正确地复制我需要的所有信息,而不必担心这个范围问题。
修改后的方法代码如下。我还将更新Github项目,以便它可以作为如何将Android Studio与NDK(非实验性)和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;