Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RSA OAEP encryption with label #1726

Closed
microshine opened this issue Jan 17, 2019 · 12 comments
Closed

RSA OAEP encryption with label #1726

microshine opened this issue Jan 17, 2019 · 12 comments

Comments

@microshine
Copy link

  • Node.js Version: 10.12.0
  • OS: MacOS 10.14.2
  • Scope (install, code, runtime, meta, other?): code
  • Module (and version) (if relevant): crypto

Is it possible to set label for RSA OAEP mechanism like WebCrypto does?

Chromium uses EVP_PKEY_CTX_set0_rsa_oaep_label function (source). But I can't find this function in NodeJS source

@rmhrisk
Copy link

rmhrisk commented Jan 21, 2019

@indutny any ideas?

@microshine
Copy link
Author

microshine commented Jan 25, 2019

WebCrypt returns correct decrypted message. NodeJs throws error

Error: error:04099079:rsa routines:RSA_padding_check_PKCS1_OAEP_mgf1:oaep decoding error

Message was encrypted in Chrome

WebCrypto decrypt

const privateKeyJwk = {
  alg: "RSA-OAEP-256",
  d: "fSHfoQe74aFcDMGNyzHkNrx2-ngPL63VSdgCxX4jlgIAQ6w7gyVTb2i48ZLv_z1fQSN8I3YWNj_TBon9WcFt87WDglTxjYLLpz9QkAMfPU1hz8zHThEU0p2FTkQZvrMh3eSg6JpsqYz2uRYtKS7v0P6HkAxqYH77RUA_ok-kdDFWRLt63hGX8tiuiXVX6VGcSe0nDhnAmbhWQPZxHmt5QRsdzpre-iewu1GS4tWt86DwGJ5I9ZHMWU1Ft_ZhnXK9sEdhBTLK-AraMRv44QJWW2FEZk1OwltH_71UXMxShVyIO2u1QpNcMPffhlw-Xc9yEeo9Ok4uMKrZ2zJxGoQumQ",
  dp: "BQwwuNd1g4jgksTmk5M51nKxukVVRo5dpSbZR1xMgCB6pRqWrrzWSRaKbQgTS9QzGobp4171ioX0ecCv2MKzMlQfwzTU6nXQtgTXsgvz7wAujp8Yy7svRviyLnsE66gPYr0e_slUrLxnwJjdngmqaskn5ntwxMfNgRYuXB3K780",
  dq: "2E5ip-h59PH3w5V-xQjTh3-2dkceE8wCRfglxWze2KkiRES7uggclBoLn3PGC-GpZ_u7ulddjfF5M1KRaZgVIMOAsxppyTxbzvY7_InVOSlYS7mKmMm7JYoP03NQRl1AFgtRBVcfN5NjmCiVooMaYapZbbcSBiya0quwpJaxv8E",
  e: "AQAB",
  ext: true,
  key_ops: ["decrypt"],
  kty: "RSA",
  n: "-svrcrqSZaVGdDXaIRU0P2uLZwObQSsrNKT6Kdxi9aOWupmVKSnduv8eOuOByFC4gHq8t59vSqpSXSA7J2DJlvJNDToJDcohSgfX2QJZxT2RasC6GgLLSPoGnySZwHuDtw2q-KCxJ3KER2hKtv7XX_Zr81ki5qMLbJ-XzPMIikgZf_XehWCacNmFLZlylUfv0OwJOz5dmyyT1BAGsZT4AYPtvUKp7L6BWZnoLd9Dtf-qFefGDXTglugXAISZl9VgaH26pD5AbBwTM84jquBfVvY5EVyMH5qE9NCO_zAHKxFEFnqZ0CXo7jFM6Yu_nWedrMLv-MpB9VETouL_T0UjKw",
  p: "_tuqNnhg1kPeAd5XqZNo3DWuJOSxe2SgDYJBIEMHlBzygZ-2GrpiNDsSdHZmifWIn8HCIIMjZhBenG7DJS-4aZRVEd4Z5O1MGMMyqlWXqHRGw_mS5ltRScHnYaquFkhj9EQwGuMZQhYviDVLH88tc32SRUsxWadN4fWiksRANzc",
  q: "--uYmFNszuvlKn8Z0Li0l31C2Qv1Dve6yU9SKwfO0dCp84MX8SsfEFW-mwnaAJ4MtY9WyuC8jGf5ww5cbMpZNQemR-TDPM6ZWIVmWrjR8CAbUa2O-ANLHYDcilBgWCL33NJkq6yaq94O5swp-iTRN9nWCYmkta0GVFHUi8nyRa0",
  qi: "qAUmxSdqa4S_fculRVx3sPXQwdcgwkq0s9PYob-i_zlTpDmx5LeaP1oF2smkhTDwlLdpU6omtc9iQWApJqc93ud-YReJ5ymRLDjDFc-kXx_e1RY9zgzAMrjYjUDcNOdtywx8Pjo5cYnrk1d7MbQ6bSRE2gXcoooKRQ636KAkAg4",
}

const encryptedBlob = "7FkwmlAy0aWQphok3SphAu6lsDBtWi17fR9wlLgHAd5MHYZJ/zh9C7kJuoGnSD/ZN81GSCtlmWO+Vjmqsy3Qjfuhx125R3kWuL1aKIsdMKGrGCNhYxTPOvHwVH0uZVqvpUCp/NQS1c8qEZ5VaTg1TDuQd6hAM8L6pPEdVzFNiei4re6XGVLZTGG040pNHrCqv9iQcxKaM87yUpylTfRl1N13esd+zHcouBHWG9kMxjAnNU9467V4K1q4/YoRjAVQ644+2KP2fcupTIRbcqD/WqvofOV1T/7AiGzeo4vUiU++XTBAr08MEyPTnFLgmAhzIfAwLOaoL0PvteKXfPfkFg==";

function fromBinary(text) {
  const stringLength = text.length;
  const resultView = new Uint8Array(stringLength);
  for (let i = 0; i < stringLength; i++) {
    resultView[i] = text.charCodeAt(i);
  }
  return resultView.buffer;
}

function fromBase64(base64Text) {
  base64Text = base64Text.replace(/\n/g, "").replace(/\r/g, "").replace(/\t/g, "").replace(/\s/g, "");
  if (typeof atob !== "undefined") {
    return fromBinary(atob(base64Text));
  } else {
    return new Uint8Array(Buffer.from(base64Text, "base64")).buffer;
  }
}

async function main() {
  const key = await crypto.subtle.importKey("jwk", privateKeyJwk, { name: "RSA-OAEP", hash: "SHA-256" }, false, ["decrypt"]);
  const data = await crypto.subtle.decrypt("RSA-OAEP", key, fromBase64(encryptedBlob));

  console.log(new Uint8Array(data)); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
}

main()
  .catch((err) => {
    console.error(err);
  });

NodeJS decrypt

const crypto = require("crypto");

const privateKeyBlob = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQD6y+tyupJlpUZ0NdohFTQ/a4tnA5tBKys0pPop3GL1o5a6mZUpKd26/x4644HIULiAery3n29KqlJdIDsnYMmW8k0NOgkNyiFKB9fZAlnFPZFqwLoaAstI+gafJJnAe4O3Dar4oLEncoRHaEq2/tdf9mvzWSLmowtsn5fM8wiKSBl/9d6FYJpw2YUtmXKVR+/Q7Ak7Pl2bLJPUEAaxlPgBg+29QqnsvoFZmegt30O1/6oV58YNdOCW6BcAhJmX1WBofbqkPkBsHBMzziOq4F9W9jkRXIwfmoT00I7/MAcrEUQWepnQJejuMUzpi7+dZ52swu/4ykH1UROi4v9PRSMrAgMBAAECggEAfSHfoQe74aFcDMGNyzHkNrx2+ngPL63VSdgCxX4jlgIAQ6w7gyVTb2i48ZLv/z1fQSN8I3YWNj/TBon9WcFt87WDglTxjYLLpz9QkAMfPU1hz8zHThEU0p2FTkQZvrMh3eSg6JpsqYz2uRYtKS7v0P6HkAxqYH77RUA/ok+kdDFWRLt63hGX8tiuiXVX6VGcSe0nDhnAmbhWQPZxHmt5QRsdzpre+iewu1GS4tWt86DwGJ5I9ZHMWU1Ft/ZhnXK9sEdhBTLK+AraMRv44QJWW2FEZk1OwltH/71UXMxShVyIO2u1QpNcMPffhlw+Xc9yEeo9Ok4uMKrZ2zJxGoQumQKBgQD+26o2eGDWQ94B3lepk2jcNa4k5LF7ZKANgkEgQweUHPKBn7YaumI0OxJ0dmaJ9YifwcIggyNmEF6cbsMlL7hplFUR3hnk7UwYwzKqVZeodEbD+ZLmW1FJwedhqq4WSGP0RDAa4xlCFi+INUsfzy1zfZJFSzFZp03h9aKSxEA3NwKBgQD765iYU2zO6+UqfxnQuLSXfULZC/UO97rJT1IrB87R0KnzgxfxKx8QVb6bCdoAngy1j1bK4LyMZ/nDDlxsylk1B6ZH5MM8zplYhWZauNHwIBtRrY74A0sdgNyKUGBYIvfc0mSrrJqr3g7mzCn6JNE32dYJiaS1rQZUUdSLyfJFrQKBgAUMMLjXdYOI4JLE5pOTOdZysbpFVUaOXaUm2UdcTIAgeqUalq681kkWim0IE0vUMxqG6eNe9YqF9HnAr9jCszJUH8M01Op10LYE17IL8+8ALo6fGMu7L0b4si57BOuoD2K9Hv7JVKy8Z8CY3Z4JqmrJJ+Z7cMTHzYEWLlwdyu/NAoGBANhOYqfoefTx98OVfsUI04d/tnZHHhPMAkX4JcVs3tipIkREu7oIHJQaC59zxgvhqWf7u7pXXY3xeTNSkWmYFSDDgLMaack8W872O/yJ1TkpWEu5ipjJuyWKD9NzUEZdQBYLUQVXHzeTY5golaKDGmGqWW23EgYsmtKrsKSWsb/BAoGBAKgFJsUnamuEv33LpUVcd7D10MHXIMJKtLPT2KG/ov85U6Q5seS3mj9aBdrJpIUw8JS3aVOqJrXPYkFgKSanPd7nfmEXiecpkSw4wxXPpF8f3tUWPc4MwDK42I1A3DTnbcsMfD46OXGJ65NXezG0Om0kRNoF3KKKCkUOt+igJAIO";
const encryptedBlob = "7FkwmlAy0aWQphok3SphAu6lsDBtWi17fR9wlLgHAd5MHYZJ/zh9C7kJuoGnSD/ZN81GSCtlmWO+Vjmqsy3Qjfuhx125R3kWuL1aKIsdMKGrGCNhYxTPOvHwVH0uZVqvpUCp/NQS1c8qEZ5VaTg1TDuQd6hAM8L6pPEdVzFNiei4re6XGVLZTGG040pNHrCqv9iQcxKaM87yUpylTfRl1N13esd+zHcouBHWG9kMxjAnNU9467V4K1q4/YoRjAVQ644+2KP2fcupTIRbcqD/WqvofOV1T/7AiGzeo4vUiU++XTBAr08MEyPTnFLgmAhzIfAwLOaoL0PvteKXfPfkFg==";
const encrypted = Buffer.from(encryptedBlob, "base64");

const msg = crypto.privateDecrypt(
  {
    key: `-----BEGIN PRIVATE KEY-----\n${privateKeyBlob}\n-----END PRIVATE KEY-----`,
    padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
  },
  encrypted,
);
console.log(msg);

@indutny
Copy link
Member

indutny commented Jan 25, 2019

cc @nodejs/crypto looks like a missing feature.

@microshine
Copy link
Author

@indutny any chance to fix it?

@sam-github
Copy link

@microshine Its a reasonable feature, I can't think why we wouldn't accept a PR. If you don't want to work on it, you'll have to wait until someone picks this up.

@microshine
Copy link
Author

@sam-github I'll check if I can do PR. Which branch should I use for PR?

I did one more test and found that NodeJS uses SHA-1 for RSA-OAEP encryption without label parameter.

Here is WebCrypto example which allows to decrypt encrypted message from NodeJS

const key = await crypto.subtle.importKey("jwk", privateKeyJwk, { name: "RSA-OAEP", hash: "SHA-1" }, false, ["decrypt"]);
const data = await crypto.subtle.decrypt("RSA-OAEP", key, fromBase64(encryptedBlob));

@tniessen
Copy link
Member

@microshine There are instructions for creating PRs in the main repository. Usually, you should create a PR against the master branch. Let me know if you need any help, I have been working on the crypto module a lot over the last couple of months.

In your WebCrypto example, do you supply the label during decryption?

@microshine
Copy link
Author

@tniessen thank you

I don't supply the label

const data = await crypto.subtle.decrypt("RSA-OAEP", key, fromBase64(encryptedBlob));
//                                       ^^^^^^^^^^

with label it will look like

const data = await crypto.subtle.decrypt({name: "RSA-OAEP", label: new Uint8Arrau([1, 2, 3, 4, 5])}, key, fromBase64(encryptedBlob));

@tniessen
Copy link
Member

@microshine That's what I thought. So the difference in this case is that node throws without the tag while WebCrypto silently ignores it, is that right?

@tniessen
Copy link
Member

This has been implemented in Node.js and is available in recent releases.

@aseevia
Copy link

aseevia commented Dec 20, 2019

@sam-github I'll check if I can do PR. Which branch should I use for PR?

I did one more test and found that NodeJS uses SHA-1 for RSA-OAEP encryption without label parameter.

Here is WebCrypto example which allows to decrypt encrypted message from NodeJS

const key = await crypto.subtle.importKey("jwk", privateKeyJwk, { name: "RSA-OAEP", hash: "SHA-1" }, false, ["decrypt"]);
const data = await crypto.subtle.decrypt("RSA-OAEP", key, fromBase64(encryptedBlob));

@microshine thanks for this insight, I've been fighting with the similar problem for a week!

@koteisaev
Copy link

koteisaev commented Aug 13, 2023

I did one more test and found that NodeJS uses SHA-1 for RSA-OAEP encryption without label parameter.
Adding this for those who will land here due to oaep formatting error decrypting message from web crypto api.

The options object for privateDecript and publicEncrypt has now (NodeJS 18.17.1 onwards?) options like

                            oaepHash: this.#algorithm.hash, //e. g. "SHA-512"
                            oaepLabel: getDefaultOAEPLabel() // an empty Uint8Array is ok too

that can be specified among the key.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants