【问题标题】:Signing using Curve25519 in Crypto++在 Crypto++ 中使用 Curve25519 签名
【发布时间】:2019-03-04 05:25:35
【问题描述】:

我正在我的一个项目中实施 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

我希望你们中的任何人都可以向我解释。

提前致谢!!

【问题讨论】:

  • 请在代码之外解释您尝试实现的协议以及失败的位置和方式。调试加密通常是以十六进制打印并比较内容。如果 HMAC 的输入值不同,那么显然是有问题的。然后,您将不得不使用更多的打印语句来追溯差异的来源。
  • 代码中没有使用该密钥长度。无论如何,它来自crypto++中的一个例子。尽管我还不知道我没有获得正确的共享密钥的方式,但我得到了与您的实现一起使用的代码。也许是因为我没有使用同意部分。
  • @Ruben - “我让代码工作了......” - 你应该发布一个答案,然后接受你自己的答案。它将使未来的游客受益。另见How does accepting an answer work?

标签: c++ cryptography hmac crypto++ elliptic-curve


【解决方案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 是伯恩斯坦使用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 在 cmets 中指出的那样,您没有描述您正在尝试做什么或陈述算法。我将不理会它。

通常在您的用例中发生的情况是,您使用 Diffie-Hellman 得出一个共享密钥。然后为分组密码或流密码和 MAC 派生几个密钥。您通常使用HDKF 之类的东西来执行此操作。最后,您键入密码和 mac,然后执行批量加密。

我将冒险提出一个建议,即一旦您与ecdhA.Agree(sharedA, privA, pubB) 和/或ecdhB.Agree(sharedB, privB, pubA) 共享密钥,使用HKDF 派生密钥,然后使用派生密钥来密钥ChaCha20Poly1305XChaCha20Poly1305(或另一个authenticated encryption模式密码)

当使用ChaCha20Poly1305XChaCha20Poly1305 时,每条消息都应该(必须!)获得一个唯一的随机数。只需运行一个计数器并在每条消息后递增它。

【讨论】:

  • 非常感谢您的回答。这真的很有帮助。我有一个关于 ED25519 的问题。如果它使用的是 x25519,它不应该使用您使用您的私钥和接收者的公钥生成的共享密钥进行签名吗?我认为 Curve25519 对对称算法很有用,因为共享您使用唯一的共享密钥进行验证。
  • 我也对类似的东西感兴趣。据我了解,A 和 B 两方共享各自的公钥。然后,他们生成一个共享秘密。他们可以使用 HDKF 从共享密钥中提取密钥。我想一旦他们都拥有这个密钥,他们就可以使用任何类型的对称加密相互通信。如果这是正确的,我想知道为什么他们不能只使用共享密钥作为密钥
  • 我认为您可能会混淆 x25519(密钥协议)、ed25519(签名)和 curve25519(基础数学领域;由 x25519 和 ed25519 使用)。 ed25519 始终使用私钥签名。 ed25519 密钥是识别私钥持有者的长期密钥。
  • 我不明白你在用 x25519 的共享密钥做什么。 x25519 是一个关键协议方案,如ECDH(也是椭圆曲线)或DH(整数)。 x25519 密钥是临时的,协议的每次运行都应该使用一个新的随机密钥。
【解决方案2】:

最后,感谢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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多