我正在我的一个项目中实现Curve25519。我以为可以将其与HMAC,CMAC或其他算法结合起来进行签名和验证。问题在于Curve25519无法生成相同的共享密钥。
我对密码学不是很了解,也不知道我是在做错什么,还是不能将Curve25519与HMAC结合使用。
这是我准备的测试代码。
#include "xed25519.h"
using CryptoPP::x25519;
#include "donna.h"
using CryptoPP::Donna::curve25519_mult;
using CryptoPP::Donna::ed25519_sign;
using CryptoPP::Donna::ed25519_sign_open;
using CryptoPP::Donna::ed25519_publickey;
#include "filters.h"
#include "osrng.h"
#include "cryptlib.h"
#include "files.h"
#include "hex.h"
#include "sha.h"
#include "hmac.h"
using namespace std;
static const int SECRET_KEYLENGTH=32;
static const int PRIVATE_KEYLENGTH=128;
static const int PUBLIC_KEYLENGTH=32;
#include <string>
#include <iostream>
int main(int argc, char* argv[])
{
using namespace CryptoPP;
AutoSeededRandomPool prng, prng2;
byte *aux ;// new byte[PRIVATE_KEYLENGTH];
byte privateKey[PUBLIC_KEYLENGTH];
byte publicKey[PUBLIC_KEYLENGTH];
// Node 1
x25519 x(privateKey, publicKey) ; //( const byte y[PUBLIC_KEYLENGTH],const byte x[SECRET_KEYLENGTH]
cout << "1- Generating private key " << endl;
aux = new byte;
x.GeneratePrivateKey(prng, privateKey);
Integer intPrvK(privateKey, sizeof(privateKey));
cout << "Private key created " << intPrvK << endl;
cout << "1- Generating public key " << endl;
//void GeneratePublicKey (RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const
if(curve25519_mult(privateKey, publicKey) == 0 ){
Integer intPubK(publicKey, sizeof(publicKey));
cout << "1- Public key created " << intPubK << endl;
//cout << "1- The new public key is " << privateKey << endl;
}
else
cout << "curve25519_mult did not work " << endl;
//Node 2
byte privateKey2[PUBLIC_KEYLENGTH];
byte publicKey2[PUBLIC_KEYLENGTH];
x25519 y(privateKey2, publicKey2) ; //( const byte y[PUBLIC_KEYLENGTH],const byte x[SECRET_KEYLENGTH]
cout << "2- Generating private key " << endl;
aux = new byte;
y.GeneratePrivateKey(prng2, privateKey2);
Integer intPrvK2(privateKey2, sizeof(privateKey2));
cout << "2- Private key created " << intPrvK2 << endl;
cout << "2- Generating public key " << endl;
//void GeneratePublicKey (RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const
if(curve25519_mult(privateKey2, publicKey2) == 0 ){
Integer intPubK2(publicKey2, sizeof(publicKey2));
cout << "2- Public key created " << intPubK2 << endl;
//cout << "2- The new public key is " << privateKey2 << endl;
}
else
cout << "2- curve25519_mult did not work " << endl;
cout << "\nGenerations of shared keys" << endl;
/*int curve25519_mult ( byte sharedKey[32],
const byte secretKey[32],
const byte othersKey[32]
) */
byte sharedKey1_2[PUBLIC_KEYLENGTH];
byte sharedKey2_1[PUBLIC_KEYLENGTH];
if( curve25519_mult(sharedKey1_2, privateKey, publicKey2) == 0){
Integer intSharedKey1_2(sharedKey1_2, sizeof(sharedKey1_2));
cout << "1- Shared key created " << intSharedKey1_2 << endl;
}
if( curve25519_mult(sharedKey2_1, privateKey2, publicKey) == 0){
Integer intSharedKey2_1(sharedKey2_1, sizeof(sharedKey2_1));
cout << "2- Shared key created " << intSharedKey2_1 << endl;
}
// We have two keys each.
string plain = "\n\nHMAC Test";
string mac, encoded;
/*********************************\
\*********************************/
cout << "plain text: " << plain << endl;
/*********************************\
\*********************************/
try
{
HMAC< SHA256 > hmac(sharedKey1_2, PUBLIC_KEYLENGTH);
StringSource ss2(plain, true,
new HashFilter(hmac,
new StringSink(mac)
) // HashFilter
); // StringSource
}
catch(const CryptoPP::Exception& e)
{
cerr << e.what() << endl;
exit(1);
}
/*********************************\
\*********************************/
// Pretty print
encoded.clear();
StringSource ss3(mac, true,
new HexEncoder(
new StringSink(encoded)
) // HexEncoder
); // StringSource
cout << "hmac: " << encoded << endl;
try
{
HMAC< SHA256 > hmac2(sharedKey2_1, PUBLIC_KEYLENGTH);
const int flags = HashVerificationFilter::THROW_EXCEPTION | HashVerificationFilter::HASH_AT_END;
StringSource(plain + mac, true,
new HashVerificationFilter(hmac2, NULL, flags)
); // StringSource
cout << "Verified message" << endl;
}
catch(const CryptoPP::Exception& e)
{
cerr << e.what() << endl;
}
return 0;
}
编辑:这是此代码的输出:
1- Generating private key
Private key created 3951427468589058657788500055898583055730859037456996206614247149081707227760.
1- Generating public key
1- Public key created 2713877106980505211026290261997698325438191786766062178625865092937394618368.
2- Generating private key
2- Private key created 58089620826126204922773651760985512282935010454438059044416143831910823682427.
2- Generating public key
2- Public key created 1185077373537344710091841384487531087158005785833397747712.
Generations of shared keys
1- Shared key created 32717475549536125870454478996763331991259932599267432219938737089203052157444.
2- Shared key created 83438083910146518364399797164490155462911710345063602550172142504835353991253.
plain text:
HMAC Test
hmac: 27C84FED802319639DF86D36E43090666D6CB20F556778B90819087BC55C2249
HashVerificationFilter: message hash or MAC not valid
我希望你们中的任何人都能向我解释。
提前谢谢!
答案 0 :(得分:1)
问题在于Curve25519无法生成相同的共享密钥...
curve25519描述了基础字段。 x25519是该领域的关键协议。协议的每次运行都会产生一个新的共享机密。这是因为每次运行协议时,都会使用一组新的随机参数。
以下是您与x25519进行密钥协商的方式。 x25519是Bernstein使用curve25519的关键协议方案。下面的示例代码摘自Crypto++ wiki。
首先,创建一些临时密钥:
AutoSeededRandomPool rndA, rndB;
x25519 ecdhA(rndA), ecdhB(rndB);
SecByteBlock privA(ecdhA.PrivateKeyLength());
SecByteBlock pubA(ecdhA.PublicKeyLength());
ecdhA.GenerateKeyPair(rndA, privA, pubA);
SecByteBlock privB(ecdhB.PrivateKeyLength());
SecByteBlock pubB(ecdhB.PublicKeyLength());
ecdhB.GenerateKeyPair(rndB, privB, pubB);
第二,设置共享机密缓冲区:
SecByteBlock sharedA(ecdhA.AgreedValueLength());
SecByteBlock sharedB(ecdhB.AgreedValueLength());
第三,执行协议协议:
if(!ecdhA.Agree(sharedA, privA, pubB))
throw std::runtime_error("Failed to reach shared secret (1)");
if(!ecdhB.Agree(sharedB, privB, pubA))
throw std::runtime_error("Failed to reach shared secret (2)");
最后,您可以检查按键:
HexEncoder encoder(new FileSink(std::cout));
std::cout << "Shared secret (A): ";
StringSource(sharedA, sharedA.size(), true, new Redirector(encoder));
std::cout << std::endl;
std::cout << "Shared secret (B): ";
StringSource(sharedB, sharedB.size(), true, new Redirector(encoder));
std::cout << std::endl;
示例输出如下。
$ ./test.exe
Shared secret (A): B5C105BC3B685869AFBDFE64F15D27D6D0EAAA1A22F03B45B86E09FC76522450
Shared secret (B): B5C105BC3B685869AFBDFE64F15D27D6D0EAAA1A22F03B45B86E09FC76522450
“创建一些临时密钥...” 具有很多豁免权。您仍然需要将临时密钥的公共部分发送给另一方。并且发送给另一方的临时公钥应该经过签名,以便另一方知道它是真实的。
...签名并验证...
这是您使用ed25519签名方案签名的方式。 ed25519是Bernstein使用curve25519的签名方案。下面的示例代码摘自Crypto++ wiki。
首先,创建您的签名密钥:
ed25519::Signer signer;
signer.AccessPrivateKey().GenerateRandom(prng);
第二,保存您的签名密钥:
FileSink fs("private.key.bin");
signer.GetPrivateKey().Save(fs);
第三,使用私钥签名消息:
AutoSeededRandomPool prng;
HexEncoder encoder(new FileSink(std::cout));
std::string message = "Yoda said, Do or do not. There is no try.";
std::string signature;
// Determine maximum signature size
size_t siglen = signer.MaxSignatureLength();
signature.resize(siglen);
// Sign, and trim signature to actual size
siglen = signer.SignMessage(prng, (const byte*)&message[0], message.size(), (byte*)&signature[0]);
signature.resize(siglen);
// Print signature to stdout
std::cout << "Signature: ";
StringSource(signature, true, new Redirector(encoder));
std::cout << "\n" << std::endl;
示例输出如下。
$ ./test.exe
Signature: B8EABDAA754BBCDC0B11ADE1FBA52CE39CD52FF42DE95E44CA6103652171468B63446
81DFB09F0D556EBF01BE43064D90C76711D9E1FF0FD3C41AF843DF17909
您可以使用以下代码保存您的公钥。然后,将您的公钥提供给其他人。
ed25519::Signer signer;
...
ed25519::Verifier verifier(signer);
FileSink fs("public.key.bin");
verifier.GetPublicKey().Save(fs);
...我想我可以将其与HMAC,CMAC或其他算法[用于签名]结合起来
我不确定要提出的方案是什么,还是使用HMAC和CMAC。正如@Maarten在评论中指出的那样,您不会描述您要执行的操作或陈述算法。我将不理会它。
通常,用例中发生的事情是,您使用Diffie-Hellman获得了一个共享机密。然后,您可以为分组密码或流密码和MAC导出几个密钥。通常,您可以使用HDKF
之类的方法来执行此操作。最后,您对密码和mac进行加密,然后执行批量加密。
如果您与ecdhA.Agree(sharedA, privA, pubB)
和/或ecdhB.Agree(sharedB, privB, pubA)
有一个共享的机密,请使用HKDF
派生密钥,然后使用派生的密钥来签发{{3 }}或ChaCha20Poly1305
(或其他XChaCha20Poly1305
模式密码)
使用authenticated encryption或ChaCha20Poly1305
时,每条消息都必须(必须!)具有唯一的随机数。只需运行一个计数器,然后在每条消息后将其递增即可。
答案 1 :(得分:1)
最后,由于jww的回答,我找到了解决方案。这个问题是我在生成密钥时所预期的,虽然我不确定如何使用先前的功能解决问题,但是代码可能缺少了密钥之间的约定部分。
这里有一个使用x25519密钥交换实现HMAC签名的工作代码示例。
//g++ -g3 -ggdb -O0 Curve25519_HMAC_2.cpp -o Curve25519_HMAC_2.exe -lpthread -I/usr/local/include/cryptopp -L/usr/local/lib -l cryptopp
//g++ -DNDEBUG -g -g3 -O2 -Wall -Wextra -o Curve25519_HMAC_2 Curve25519_HMAC_2.cpp -I/usr/local/include/cryptopp -L/usr/local/lib -l cryptopp
#include "xed25519.h"
using CryptoPP::x25519;
#include "donna.h"
using CryptoPP::Donna::curve25519_mult;
using CryptoPP::Donna::ed25519_sign;
using CryptoPP::Donna::ed25519_sign_open;
using CryptoPP::Donna::ed25519_publickey;
#include "filters.h"
#include "osrng.h"
#include "cryptlib.h"
#include "files.h"
#include "hex.h"
#include "sha.h"
#include "hmac.h"
using namespace std;
static const int SECRET_KEYLENGTH=32;
static const int PRIVATE_KEYLENGTH=128;
static const int PUBLIC_KEYLENGTH=32;
#include <string>
#include <iostream>
int main(int argc, char* argv[])
{
using namespace CryptoPP;
AutoSeededRandomPool rndA, rndB;
x25519 ecdhA(rndA), ecdhB(rndB);
SecByteBlock privA(ecdhA.PrivateKeyLength());
SecByteBlock pubA(ecdhA.PublicKeyLength());
ecdhA.GenerateKeyPair(rndA, privA, pubA);
SecByteBlock privB(ecdhB.PrivateKeyLength());
SecByteBlock pubB(ecdhB.PublicKeyLength());
ecdhB.GenerateKeyPair(rndB, privB, pubB);
SecByteBlock sharedA(ecdhA.AgreedValueLength());
SecByteBlock sharedB(ecdhB.AgreedValueLength());
if(!ecdhA.Agree(sharedA, privA, pubB))
throw std::runtime_error("Failed to reach shared secret (1)");
if(!ecdhB.Agree(sharedB, privB, pubA))
throw std::runtime_error("Failed to reach shared secret (2)");
HexEncoder encoder(new FileSink(std::cout));
std::cout << "Shared secret (A): ";
StringSource(sharedA, sharedA.size(), true, new Redirector(encoder));
std::cout << std::endl;
std::cout << "Shared secret (B): ";
StringSource(sharedB, sharedB.size(), true, new Redirector(encoder));
std::cout << std::endl;
// We have two keys each.
string plain = "\n\nHMAC Test";
string mac, encoded;
/*********************************\
\*********************************/
cout << "plain text: " << plain << endl;
/*********************************\
\*********************************/
try
{
HMAC< SHA256 > hmac(sharedA, sharedA.size());
StringSource ss2(plain, true,
new HashFilter(hmac,
new StringSink(mac)
) // HashFilter
); // StringSource
}
catch(const CryptoPP::Exception& e)
{
cerr << e.what() << endl;
exit(1);
}
/*********************************\
\*********************************/
// Pretty print
encoded.clear();
StringSource ss3(mac, true,
new HexEncoder(
new StringSink(encoded)
) // HexEncoder
); // StringSource
cout << "hmac: " << encoded << endl;
try
{
HMAC< SHA256 > hmac2(sharedB, sharedB.size());
const int flags = HashVerificationFilter::THROW_EXCEPTION | HashVerificationFilter::HASH_AT_END;
StringSource(plain + mac, true,
new HashVerificationFilter(hmac2, NULL, flags)
); // StringSource
cout << "Verified message" << endl;
}
catch(const CryptoPP::Exception& e)
{
cerr << e.what() << endl;
}
return 0;
}
这是输出:
Shared secret (A): 284FE14022541BD8939C40249A3805DB6C4548B01FF0826253E6FAC53C489D46
Shared secret (B): 284FE14022541BD8939C40249A3805DB6C4548B01FF0826253E6FAC53C489D46
plain text:
HMAC Test
hmac: BCFEE5E6CCA6EB9818D961DA22545CE9989E799430AA54E9EDBEF35A244D4C77
Verified message