javascriptcryptographyelliptic-curvesubtlecrypto

crypto.subtle.sign() doesn't seem to be doing any signing?


My code:

/*
Fetch the contents of the "message" textbox, and encode it
in a form we can use for the sign operation.
*/
function getMessageEncoding() {
  const messageBox = 'hello world!';
  let message = messageBox.value;
  let enc = new TextEncoder();
  return enc.encode(message);
}

async function main() {
    let encoded = getMessageEncoding();
    window.crypto.subtle.sign(
      {
        name: "ECDSA",
        hash: { name: "SHA-384" },
      },
     {"alg":"ES256","crv":"P-256","d":"KRiXxoIFHmBQJdoCsqB8Bc9_f2Z-QCdRgoydMKdoL04","ext":true,"key_ops":["sign"],"kty":"EC","x":"FKwqyGd4i2NAl8RUXCCBRCAIbcpeGyfyXwgA_AWHb8Y","y":"njxhw5O6zGVkBlcPDKYj0E-6VO1giHTUkJWBhgKNqd8"},
  encoded,
    ).then(signature => {
    console.log(signature);
    alert('test');
  });
  alert(signature);
}

main();

(the private key is a dummy private key and not a production one)

When I run it it doesn't do anything. I'd expect it to put the signature into the JS console?

Here it is on JS Fiddle:

https://jsfiddle.net/pwuersfL/1/

Any ideas?


Solution

  • In the posted code the key import with importKey() is missing. importKey() converts the JWK into a CryptoKey which can be processed by sign().

    In the following code the missing import is added (plus verifying):

    function getMessageEncoding() {
        //const messageBox = 'hello world!';
        //let message = messageBox.value;
        let message = 'hello world!'; // for testing
        let enc = new TextEncoder();
        return enc.encode(message);
    }
    
    async function main() {
        
        // sign
        let encoded = getMessageEncoding();
        let jwk = {"alg":"ES256","crv":"P-256","d":"KRiXxoIFHmBQJdoCsqB8Bc9_f2Z-QCdRgoydMKdoL04","ext":true,"key_ops":["sign"],"kty":"EC","x":"FKwqyGd4i2NAl8RUXCCBRCAIbcpeGyfyXwgA_AWHb8Y","y":"njxhw5O6zGVkBlcPDKYj0E-6VO1giHTUkJWBhgKNqd8"};
        let key =  await window.crypto.subtle.importKey("jwk", jwk, {name: "ECDSA", namedCurve: "P-256"},true,["sign"]); // Fix: import the key 
        let signature = await window.crypto.subtle.sign(
            {
                name: "ECDSA",
                hash: { name: "SHA-384" },
            },
            key,
            encoded,
        );
        console.log(ab2hex(signature));
        
        // verify
        jwk = {"alg":"ES256","crv":"P-256","ext":true,"key_ops":["verify"],"kty":"EC","x":"FKwqyGd4i2NAl8RUXCCBRCAIbcpeGyfyXwgA_AWHb8Y","y":"njxhw5O6zGVkBlcPDKYj0E-6VO1giHTUkJWBhgKNqd8"};
        key =  await window.crypto.subtle.importKey("jwk", jwk, {name: "ECDSA", namedCurve: "P-256"},true,["verify"]); 
        let verified = await window.crypto.subtle.verify(
            {
                name: "ECDSA",
                hash: { name: "SHA-384" },
            },
            key,
            signature,
            encoded,
        );
        console.log(verified);
    }
     
    // helper: hex encoding
    function ab2hex(ab) { 
        return Array.prototype.map.call(new Uint8Array(ab), x => ('00' + x.toString(16)).slice(-2)).join('');
    }
    
    main();

    The code produces for P-256 a 64 bytes signature in IEEE P1363 format (r|s), which can be successfully verified with the public key.