Creation of ECDSA public key given curve and publi

2020-06-27 20:27发布

问题:

I am struggling with the creation of a ECDSA public key from a string representation of a public key i.e

string     devicePublicKey("86FB5EB3CA0507226BE7197058B9EC041D3A3758D9D9C91902ACA3391F4E58AEF13AFF63CC4EF68942B9B94904DC1B890EDBEABD16B992110624968E894E560E");

previously I found that I had to prefix this key with '04' so not sure if this is require this time?

I am trying to generate it to use in verifying a signature

string ecs04b2ExpSignature("0199E984CEC75DDCA7F1DDF6E53E2E67352A2BE38A4B66F8ED596606FAB983FF300CAA76DE88CED9D563A5C03E8F3A7C000780F3F2061C611E9AA0B18B460D77");

where the data to be signed is

string      ecs04b2SigningData("020000000000000001FFFFFFFFFFFFFFFE123456789ABCDEF000B3DA2000000100000300000003030003000300");

My rough code for now looks like this

SecByteBlock message(convertHexStrToSecByteBlock(messageIn));
SecByteBlock signature(convertHexStrToSecByteBlock(signatureIn));

ECDSA<ECP, SHA256>::PublicKey publicKey;
string inPublicKey("04");
inPublicKey.append(pubKeyIn);
SecByteBlock pubKey = encryptBase::convertHexStrToSecByteBlock(inPublicKey);



ECP::Point p;
publicKey.AccessGroupParameters().Initialize(CryptoPP::ASN1::secp256r1());
publicKey.GetGroupParameters().GetCurve().DecodePoint(p, pubKey, publicKey.GetGroupParameters().GetCurve().EncodedPointSize(true));
publicKey.SetPublicElement(p);

//ByteQueue qt;
//qt.Put((byte*)exp.c_str(),(size_t)exp.size());
AutoSeededRandomPool prng;
bool result = publicKey.Validate(prng, 3);
if (result) 
{  
    // Load public key (in ByteQueue, X509 format)
    ECDSA<ECP, SHA256>::Verifier verifier(publicKey);

    bool result = verifier.VerifyMessage(message.data(), messageIn.size(), signature.data(), signature.size());
    if (result)
        cout << "Verified signature on message" << endl;
    else
        cerr << "Failed to verify signature on message" << endl;
}
else
{
    cout << "Failed to validate key" << endl;
}

this is chopped together so wont build. Any help would be great

PS I asked a similar question relating to private keys here Creation of ECDSA private key given curve and private exponent?

回答1:

The answer is detailed on the ECDSA wiki page, but its not readily apparent. You need to perform the following to initialize the publicKey given the curve and public point:

string pt = "2DB45A3F21889438B42C8F464C75292BACF5FDDB5DA0B492501B299CBFE92D8F"
            "DB90FC8FF4026129838B1BCAD1402CAE47FE7D8084E409A41AFCE16D63579C5F";

HexDecoder decoder;
decoder.Put((byte*)pt.data(), pt.size());
decoder.MessageEnd();

ECP::Point q;
size_t len = decoder.MaxRetrievable();
// len should be GetField().MaxElementByteLength()

q.identity = false;
q.x.Decode(decoder, len/2);
q.y.Decode(decoder, len/2);

ECDSA<ECP, SHA256>::PublicKey publicKey;
publicKey.Initialize(ASN1::secp256r1(), q);

bool result = publicKey.Validate( prng, 3 );
if( result )
{
    cout << "Validated public key" << endl;
}
else
{
    cerr << "Failed to validate public key" << endl;
    exit(1);
}

const ECP::Point& qq = publicKey.GetPublicElement();
cout << "Q.x: " << std::hex << qq.x << endl;
cout << "Q.y: " << std::hex << qq.y << endl;

The program above produces the following results.

$ ./cryptopp-test.exe
Validated public key
Q.x: 2db45a3f21889438b42c8f464c75292bacf5fddb5da0b492501b299cbfe92d8fh
Q.y: db90fc8ff4026129838b1bcad1402cae47fe7d8084e409a41afce16d63579c5fh

You can't use GetField().MaxElementByteLength() because the only thing available are the x and y coordinates. Things like field size won't be available until you initialize the underlying DL_GroupParameters_EC< EC > in the public key.

As an example, the following causes a segmentation fault:

ECDSA<ECP, SHA256>::PublicKey publicKey;
unsigned int u = publicKey.GetGroupParameters().GetCurve().GetField().MaxElementByteLength();
cout << "Field element length: " << u << endl;

You can tamper with the public key to ensure a validation failure with:

q.y.Decode(decoder, len/2);
q.y++;


回答2:

Here's the answer to your second question on how to use VerifyMessage:

...
publicKey.Initialize(ASN1::secp256r1(), q);

string msg = "020000000000000001FFFFFFFFFFFFFFFE123456789ABCDEF000B3DA2000000100000300000003030003000300";
string sig = "0199E984CEC75DDCA7F1DDF6E53E2E67352A2BE38A4B66F8ED596606FAB983FF300CAA76DE88CED9D563A5C03E8F3A7C000780F3F2061C611E9AA0B18B460D77";

string mm, ss;

decoder.Detach(new StringSink(mm));
decoder.Put((byte*)msg.data(), msg.size());
decoder.MessageEnd();

decoder.Detach(new StringSink(ss));
decoder.Put((byte*)sig.data(), sig.size());
decoder.MessageEnd();

ECDSA<ECP, SHA256>::Verifier verifier(publicKey);
result = verifier.VerifyMessage((byte*)mm.data(), mm.size(), (byte*)ss.data(), ss.size());

if( result )
{
    cout << "Verified message" << endl;
}
else
{ 
    cerr << "Failed to verify message" << endl;
    exit(1);
}

Calling Detach on the HexDecoder deletes the current filter, and replaces it with the new filter. In the code above, its the StringSink. You have to do it to ensure memory is not leaked.

Your message does not verify under the public key:

$ ./cryptopp-test.exe
Validated public key
Failed to verify message

You can also try to verify the ASCII message rather than the binary message, but it fails to verify too:

decoder.Attach(new StringSink(mm));
decoder.Put((byte*)msg.data(), msg.size());
decoder.MessageEnd();

// Swap in the ASCII message for the binary message
mm = msg;

So someone gave you the wrong message, the wrong signature or the wrong public key (or some combination).