Reputation: 73
I am using an embedded chip called an ATECC508A in order to generate a ECDSA SHA-256 public/private key, and signature for a given message.
When I read the public key from the chip, I obtain a value of:
0x4B 0x34 0x89 0xAB 0x1B 0xE2 0x4C 0x84 0xA4 0x74 0xBE 0x85 0xD9 0xCF 0x99 0xF1
0x12 0xF1 0x5E 0x13 0x67 0x17 0xB8 0x8C 0x3D 0xD8 0x54 0xC4 0x70 0xB0 0x11 0x05
0xB1 0x9E 0x4E 0x3D 0xED 0x39 0xFA 0xED 0xF3 0xDB 0x94 0x7A 0xF6 0xCE 0x3A 0x0F
0x6C 0xB1 0x86 0xB1 0x64 0x5D 0x8A 0xB8 0xA2 0x74 0x9F 0xF2 0x42 0x55 0x67 0x62
It does not start with 0x30 like a DER formatted key would have, and I am not sure if it there is any special formatting to the byte string.
To import the public key in the raw format above I do the following in javascript with the WebCrypto API (subtle crypto library):
var pubkeytest = new Uint8Array([0x4B 0x34 0x89 0xAB 0x1B 0xE2 0x4C 0x84 0xA4 0x74 0xBE 0x85 0xD9 0xCF 0x99 0xF1
0x12 0xF1 0x5E 0x13 0x67 0x17 0xB8 0x8C 0x3D 0xD8 0x54 0xC4 0x70 0xB0 0x11 0x05
0xB1 0x9E 0x4E 0x3D 0xED 0x39 0xFA 0xED 0xF3 0xDB 0x94 0x7A 0xF6 0xCE 0x3A 0x0F
0x6C 0xB1 0x86 0xB1 0x64 0x5D 0x8A 0xB8 0xA2 0x74 0x9F 0xF2 0x42 0x55 0x67 0x62]);
window.crypto.subtle.importKey(
"raw",
pubkeytest.buffer, {
name: "ECDSA",
namedCurve: "P-256",
hash: {
name: 'SHA-256'
}
},
false, //whether the key is extractable (i.e. can be used in exportKey)
["verify"] //"verify" for public key import, "sign" for private key imports
)
.then(function(pubkeytest) {
//returns a publicKey (or privateKey if you are importing a private key)
console.log(pubkeytest);
})
.catch(function(err) {
console.error(err);
});
The output in the console triggers an error on the console.error(err) line:
DOMException: Data provided to an operation does not meet requirements
I can successfully import a random public key using AES by doing the following:
const rawKey = window.crypto.getRandomValues(new Uint8Array(16));
window.crypto.subtle.importKey(
"raw",
rawKey,
"AES-GCM",
true,
["encrypt", "decrypt"]
);
The only difference I can see is the number of bytes in the array (16 vs 64) I am feeding importKey() as well as the algorithm type AES-GCM instead of ECDSA, and it does not declare P-256 or SHA-256.
How can I import the 64byte raw hex public key string above for use into javascript? I am able to perform crypto verification on signature/message using the example from here: https://github.com/mdn/dom-examples/tree/master/web-crypto
Thanks in advance
Upvotes: 2
Views: 2845
Reputation: 11
I am writing this in case someone has the same problem. The above solution (adding 0x04 to the raw key) worked perfectly for me. The problem tho is strange in my opinion. I generate the keys with OpenSSL and then import them via subtle Crypto. That must mean OpenSSL encodes the keys wrongly, which sounds odd given how popular it is. I will look further into the issue if I have the time.
Thanks for the questions and the answer, helped me greatly.
Edit: I imported the read the key file in Python and I decoded it to bytes to see if it starts with 0x04. The result was positive.
Upvotes: 1
Reputation: 73
I believe I solved the problem myself and wanted to post the solution here in case someone runs across this in the future. The issue was a simple formatting issue, but not so simple to solve since I could not find this information easily.
The 64 byte public key provided by the chip contains both the full X and Y coordinate within the key. The only thing its missing is a 0x04 prefix which is required by the ECDSA formatting rules:
https://en.bitcoin.it/wiki/Elliptic_Curve_Digital_Signature_Algorithm
public key: A number that corresponds to a private key, but does not need to be kept secret. A public key can be calculated from a private key, but not vice versa. A public key can be used to determine if a signature is genuine (in other words, produced with the proper key) without requiring the private key to be divulged. In Bitcoin, public keys are either compressed or uncompressed. Compressed public keys are 33 bytes, consisting of a prefix either 0x02 or 0x03, and a 256-bit integer called x. The older uncompressed keys are 65 bytes, consisting of constant prefix (0x04), followed by two 256-bit integers called x and y (2 * 32 bytes). The prefix of a compressed key allows for the y value to be derived from the x value.
The correct version of the public key that will work with the WebCrypto API is as follows (apologies for the lazy hex formatting below, but its the same as the question, aside from the new prefix in bold): 044B3489AB1BE24C84A474BE85D9CF99F112F15E136717B88C3DD854C470B01105B19E4E3DED39FAEDF3DB947AF6CE3A0F6CB186B1645D8AB8A2749FF242556762
Upvotes: 2