SubtleCrypto decrpyt "Data provided to an operation does not meet requirements"

149 views Asked by At

I tried to use SubtleCrypto implementation from this StackOverflow thread: Using native javascript / subtleCrypto to encrypt using RSA

I am using https://travistidwell.com/jsencrypt/demo/ to generate the keys. (only removing "RSA" from the private key as they generate it with this extra label)

Yet, I am unable to make it work bi-directional.

I always end up with "DOMException: Data provided to an operation does not meet requirements" error, but it won't tell WHAT does not meet requirements. I even cannot check the constructor, as window.crypto.subtle.decrypt is not printable, as it is [native code].

Here is the code I used. It is almost 1:1 with the code from the original thread, with only minor changes as follows:

  1. changed the order of functions for my better orientation in the code
  2. changed private/public key to ones from the generator
  3. added input params to importPublicKeyAndEncrypt and importPrivateKeyAndDecrypt function to pass what is being encrypted/decrypted. Also added return statements to those functions so I can work with the output.
  4. through various iteration, I tried adding hash:"SHA-256" and even length to match the lenght of the generated keys (bits).
  5. added chainTest function, that should, considering the things will work, encrypt the input, and decrypt it again, returning the same output as the original input.

const publicKey = `-----BEGIN PUBLIC KEY-----
MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgHgbY/2cGDeAsBUtSAbNx9fV3p1V
i8lES4vW576Hafuf4ACUVwYimYF+WMw680f1uSMYFPZiPgG4ifZrQI510Qd60mnZ
hPyXRwdBXRex8T5gZVGtu3farONvYAH9xy182rQCfMhyYJw8WD9s1h0NA0i3yGlK
zOuPrBqmoZ9i45hbAgMBAAE=
-----END PUBLIC KEY-----`;

const privateKey = `-----BEGIN PRIVATE KEY-----
MIICWwIBAAKBgHgbY/2cGDeAsBUtSAbNx9fV3p1Vi8lES4vW576Hafuf4ACUVwYi
mYF+WMw680f1uSMYFPZiPgG4ifZrQI510Qd60mnZhPyXRwdBXRex8T5gZVGtu3fa
rONvYAH9xy182rQCfMhyYJw8WD9s1h0NA0i3yGlKzOuPrBqmoZ9i45hbAgMBAAEC
gYBdSEi8rANS+CvKBsUuI5zW0VB4ufw7cUOLdHnAzMNPnrgHOy7roOvAWzT0ScSx
WvNTglylj1/BTmY4cMxO2MpjCC5MAtwM9QOaU405WatHBgCKiRKNLHCGA2KoQlJ8
let5HLYCUf+1nRtGDVrKVRBpKn2uT9ukL2SCwbo3gz59IQJBALbsjHC35srxL8rT
rnhk9O4j8zJ+uIUIobY/duODP1JOP6+gtjCYPzAivLy6+6an3M2DSjAN34XPCsEk
wrwPof0CQQCoFpwKYznNx+73CCtT4Q9mWQZlr4IzjawRFEDNZrg6ZrfWnbX0wt+M
Wv6M74leqIWr6Q7cknxXZPKSzGG0N2c3AkEAk9EdP+zr/GzgIPfj0fhLELFOUiyi
sSYjf2FTklA5+CmxPxwQfb5ZuW0otR4oQyj8vbntVl2vlbKUTzWEg3HihQJARcul
nZaYMP99FayZuSmx6FC9HEolaVzBfxIG2oN1qiJu4bn5DRpCExjRrBnm05xsbPbI
SgS1huCO7S/avidnPwJAK1RFqYuawbMyl17eHFSnWvXHoUMle0Ywd1RT+dx8EI12
TJZDRWJirUZzTg+mN6Kryh3YkdtgYOvRDckFg43dug==
-----END PRIVATE KEY-----`;

    


async function chainTest(plaintext){
    var d = await importPublicKeyAndEncrypt(plaintext);
    console.log("d -> " + d);
    var e = await importPrivateKeyAndDecrypt(d);
    console.log("e -> " + e);
}





/********** import/export executable functions *******/

async function importPublicKeyAndEncrypt(plaintext) {
     
    try {
        const pub = await importPublicKey(publicKey);
        const encrypted = await encryptRSA(pub, new TextEncoder().encode(plaintext));
        const encryptedBase64 = window.btoa(ab2str(encrypted));
        return encryptedBase64;
    } catch(error) {
        console.log(error);
    }
}

    
async function importPrivateKeyAndDecrypt(ciphertextB64) {
    
    try {
        const priv = await importPrivateKey(privateKey);
        const decrypted = await decryptRSA(priv, str2ab(window.atob(ciphertextB64)));
        return decrypted;
    } catch(error) {
        console.log(error);
    }
}


/******* encrypt/decrypt **********/

async function encryptRSA(key, plaintext) {
    let encrypted = await window.crypto.subtle.encrypt(
        {
            name: "RSA-OAEP",
            hash: "SHA-256",
            length:1024,
        },
        key,
        plaintext
    );
    return encrypted;
}


async function decryptRSA(key, ciphertext) {
    let decrypted = await window.crypto.subtle.decrypt(
        {
            name: "RSA-OAEP",
            hash: "SHA-256",
            length:1024,
        },
        key,
        ciphertext
    );
    return new TextDecoder().decode(decrypted);
}


/******** imports *************/
async function importPrivateKey(pkcs8Pem) {     
    return await window.crypto.subtle.importKey(
        "pkcs8",
        getPkcs8Der(pkcs8Pem),
        {
            name: "RSA-OAEP",
            hash: "SHA-256",
            length:1024,
        },
        true,
        ["decrypt"]
    );
}

async function importPublicKey(spkiPem) {       
    return await window.crypto.subtle.importKey(
        "spki",
        getSpkiDer(spkiPem),
        {
            name: "RSA-OAEP",
            hash: "SHA-256",
            length:1024,
        },
        true,
        ["encrypt"]
    );
}



/******** filters *************/

function getSpkiDer(spkiPem){
    const pemHeader = "-----BEGIN PUBLIC KEY-----";
    const pemFooter = "-----END PUBLIC KEY-----";
    var pemContents = spkiPem.substring(pemHeader.length, spkiPem.length - pemFooter.length);
    console.log(pemContents);
    var binaryDerString = window.atob(pemContents);
    return str2ab(binaryDerString); 
}

function getPkcs8Der(pkcs8Pem){
    const pemHeader = "-----BEGIN PRIVATE KEY-----";
    const pemFooter = "-----END PRIVATE KEY-----";
    var pemContents = pkcs8Pem.substring(pemHeader.length, pkcs8Pem.length - pemFooter.length);
    var binaryDerString = window.atob(pemContents);
    return str2ab(binaryDerString); 
}


/********* helpers ***********/

function ab2str(buf) {
    return String.fromCharCode.apply(null, new Uint8Array(buf));
}

function str2ab(str) {
    const buf = new ArrayBuffer(str.length);
    const bufView = new Uint8Array(buf);
    for (let i = 0, strLen = str.length; i < strLen; i++) {
        bufView[i] = str.charCodeAt(i);
    }
    return buf;
}
    

The result is, the encrypt part works (I had logged it and it returns a hash, although, I have no back-validation that the encryption actually ran correctly and it has a valid value/output. The decryption part crashes on "DOMException: Data provided to an operation does not meet requirements".

Anyone please does know, what is wrong here?

Thank you!

1

There are 1 answers

1
Zorak On

Closing with the following solution:

The RSA generator does NOT provide compatible keys.

Found some old testing keys from my previous PowerShell implementation, that were generated from OpenSSL, and surprise, it just works flawlessly...