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?
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++;
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).