aws / aws-encryption-sdk-javascript

AWS Encryption SDK for Javascript and Node.js
Apache License 2.0
241 stars 65 forks source link

Cannot decrypt message from Amazon Connect using RawRsaKeyringNode #373

Closed iknowcss closed 2 years ago

iknowcss commented 4 years ago

I'm creating an IVR credit card payment solution in Amazon Connect and am encrypting the credit card details with the built-in Connect encryption features. I've used this blog post as a reference for my implementation. I'm trying to write a nodejs12.x Lambda which decrypts the payload from Connect.

The Lambda source code used in that project (s3://connect-sharing/DecryptCustomerInput.zip) uses a library called awsencryptionsdk-js to decrypt the payload from Connect. However, that library is not published to npmjs. Rather than copy and paste that module into my project, I'm trying to write my code to use @aws-crypto/client-node. However, I'm not able to decrypt the payload.

Here's some code which shows how I decrypt the Connect payload with my private key using the old awsencryptionsdk-js library:

const AWSEncryptionSDKHelper = require("awsencryptionsdk-js");

/**
 * @param {string} privateKeyPEM - The private key in PEM format.
 * @param {string} encryptedDataBase64 - The encrypted payload encoded in base 64 format.
 * @returns {Promise<string>} - The decrypted message.
 */
async function oldDecrypt(privateKeyPEM, encryptedDataBase64) {
  const awsEncryptionHelper = new AWSEncryptionSDKHelper(privateKeyPEM, encryptedDataBase64);
  const data = await awsEncryptionHelper.decrypt();
  const frames = data.body.frames;
  frames.sort((a, b) => parseInt(a.seq) - parseInt(b.seq));
  return frames
    .map(({ text }) => text)
    .concat([data.body.lastFrame.text])
    .join('');
}

The code above decrypts the payload and returns the message with no issues.

Below is the new code I wrote using @aws-crypto/client-node based on this example:

const { RawRsaKeyringNode, decrypt } = require('@aws-crypto/client-node');

/**
 * @param {string} privateKeyPEM - The private key in PEM format to decrypt the cipher text.
 * @param {string} base64CipherText - The cipher text in base 64 format.
 * @param {string} keyName - Name of the key used to encrypt this message.
 * @returns {Promise<string>}
 */
async function newDecrypt(privateKeyPEM, base64CipherText, keyName) {
  const keyring = new RawRsaKeyringNode({
    keyName,
    keyNamespace: 'AmazonConnect',
    rsaKey: { privateKey: privateKeyPEM }
  });

  const ciphertextBuffer = Buffer.from(base64CipherText, 'base64');
  const { plaintext, messageHeader } = await decrypt(keyring, ciphertextBuffer);
  return plaintext.toString('utf-8');
}

When I try to decrypt with newDecrypt, I get the following error stack trace:

(node:22162) UnhandledPromiseRejectionWarning: Error: unencryptedDataKey has not been set
    at Object.needs (/var/task/orchestrate_lex_conversation/node_modules/@aws-crypto/material-management/build/main/needs.js:17:15)
    at NodeDecryptionMaterial.getUnencryptedDataKey (/var/task/orchestrate_lex_conversation/node_modules/@aws-crypto/material-management/build/main/cryptographic_material.js:217:17)
    at NodeDefaultCryptographicMaterialsManager.decryptMaterials (/var/task/orchestrate_lex_conversation/node_modules/@aws-crypto/material-management-node/build/main/node_cryptographic_materials_manager.js:49:46)
    at process._tickCallback (internal/process/next_tick.js:68:7)
    at Function.Module.runMain (internal/modules/cjs/loader.js:832:11)
    at startup (internal/bootstrap/node.js:283:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:622:3)

I dug a bit deeper into this library's code and found that an exception is thrown in _unwrapKey of raw_rsa_keyring_node.js:

Error: error:04099079:rsa routines:RSA_padding_check_PKCS1_OAEP_mgf1:oaep decoding error
    at Object.privateDecrypt (internal/crypto/cipher.js:44:12)
    at RawRsaKeyringNode._unwrapKey (/var/task/orchestrate_lex_conversation/node_modules/@aws-crypto/raw-rsa-keyring-node/build/main/raw_rsa_keyring_node.js:63:45)
    at RawRsaKeyringNode._onDecrypt (/var/task/orchestrate_lex_conversation/node_modules/@aws-crypto/raw-keyring/build/main/raw_keyring_decorators.js:32:35)
    at RawRsaKeyringNode.onDecrypt (/var/task/orchestrate_lex_conversation/node_modules/@aws-crypto/material-management/build/main/keyring.js:58:38)
    ...

Based on the documentation and the examples given, I'm not sure if I'm using the API incorrectly or if there is something wrong with the payload from Connnect. I'm still going through the docs and the code to try and sort it out myself, but I wanted to reach out to see if there's something obvious I'm missing.

Below are two payloads that may be decrypted with my private key. The 1st one comes directly from Connect. The 2nd one I created myself using @aws-crypto/client-node's encrypt method. The 1st one fails, while the 2nd one works :thinking:

Connect payload The decrypted payload is 1236547891321456.

AYADeDOc+0FhSFUFo3AviGMBU3MAXwABABVhd3MtY3J5cHRvLXB1YmxpYy1rZXkAREE5RkxLMG8wS0x3a3g3MEV2SFVoTjRQQkhzL0JYaE1JQ0ZTczRXOTNmcWVONnNOQmRjQys5TEdwdFF2Mkg2RkQrUT09AAEADUFtYXpvbkNvbm5lY3QAJDRjZGI4MjZhLTI0YzktNDQzZC1iNzhiLTMyN2M4MTcwNzNjZQIAIReQbs93LrJQPGzQHV8YUciS63WhSie3Y/xbTWRl7t51Fyi57clN78ZuCGuiPYzzJmg8NualETJSq5AaAIjviXp4Bf7WyZqe2Ho70WRFCitXIBFMpJUM+WmqwehMaDy9c1p6Roulm17eyfiOg6P2qDiM4jEDctYzQ8OAAjuJuGD8BB9N4CGaOCSwp9o8uiM3BmZ/pbLlTCvXMqkg2n94rfjL3Y3s2hAuy69mhcU9mF0ZEnya4MamrKC/pd1XLZdOV0K/zeHPksIrcy00nCJ7S+Gb73ieSUWQxjXya3uyc946QGE2KduB1zTlbAwhfRs3ctpfPtkUDIzkQfxYz+iz1vw3EuG6CW/MneVcdlJBJ58uZvL7vrTaUNFXu6tqGheXRalLDPnasDApt7c3yLEL6IoeQe7j33EYxHyxoXzK062yt85HcBXnjm+Daax2oYb8s/39qjWFlZPGD0OMdmQjzCjqe+Qjw2H3cmN2H03bGvHddhg5IPNi4vb03Owor20dCrZJAz7wjeIkfgpR5ZyWDQmU/zX+AoRLa4oaTgvPsi5TVM8Qev8+a2loJpMAQVh62xQt4CJZoRVCp7GkVhZ1XquMaz+ITtarcMV29L5XVQwubAtR1OWenk1Xd2uOGyiTRw5B5cXD6CRNUs9YCcp3DfH0lpxFOd7yMZzICJxTgEgCAAAAAAwAABAABB6FU2et/38i0UOCg1Ovr77Pa0lkxlemxoeQM/////8AAAABZdNd5d67U84wbD2mAAAAEEG64b/bdb0rvm9RLVbBxfeGJZe/VP12oLpBM33B5ufjAGcwZQIwNe81SSjyNfTowZE4ll53sUdubIUvuPX9PXMGNCzImoVosr+6rZEbk3GFujj7wF0AAjEAztFBwLTAFLZPiGpQNuNRBXiVrTKhiSUwHGrnK/1UlH5ZumCmUAV2FeyD0A9UH9oa

Payload encrypted with @aws-crypto/client-node

AYADeJng3phI5lPf+MvmQ0GOf2MAXwABABVhd3MtY3J5cHRvLXB1YmxpYy1rZXkAREE4TlJ2MG02Zy94TWJ5aTJyVHU3aERKRkkwRC9DbmFFSkpzVTg2TllHbG5qVTRXY2orNkxkSmxGb0hHeDVFRkpOQT09AAEADUFtYXpvbkNvbm5lY3QAJDRjZGI4MjZhLTI0YzktNDQzZC1iNzhiLTMyN2M4MTcwNzNjZQIAnfklSo24jl95kJ4JsC0+wdcdpNhi0MD3PrWs9FiUZi0RZ+Hjj8PPm0frgrzVCQdi7JH3gU/h797buoQaN9A23VBThn644/Ps8T5X6oeZWb2DP0/O9kFCssC2vP0L7WomDvMZedxDASEJej/a4e5LJrXtAGHlJcKhPJ5NzfIo4ccjHWgRiDzu4JQKga3vRXMkUe+/4O6EjYLjCNbubAxjH+rGPGgGgA65N4bDaOKUzmqfbV5wudnigCj8la419i0MSxg80NUfm52NfxYpvO9Z1QROLX4CkLyr98h759dzz+gTLo4v/TdGiEA95f0ARjjNeyePkhLncJ8seiri9N53do6Ry6rpDz2ZZrsv32p7g+EUZHPKYfoltpuwrfZskUmzh8+H9nAnnw9tUzMHzmbVTze2mpJ0cd3TljSv56Wh8ZqBPfR2D0narADt7jFiNFMqL73a5IaD3jLDLA9S9Ax+zP+b4REm22aOoJ9diUjpbODn3CZ3tlhZnGLJGzumrKFP2I22cegOCAvvjlI/Y2GN8MjrvXYuzHjEm6tSMA1zYFxrck1h6nT+bUvjsxnPxZAsbJgGoXIJUf+Fpr24UUVUaMAdx6vkI/v9booFCLcm5qbLxrFvQlfwpwwyBzVZkgNa7qrKyQs9TuHZICFMrDQkagc4Z8QauldFdbl4Bbj83W4CAAAAAAwAABAAAAAAAAAAAAAAAAAA8wAcoQ2kiI2ZWTMwFRIWd/////8AAAABAAAAAAAAAAAAAAABAAAAEBNU9GddlBwWeq7L/Ix0eTc//qsRhhHdOob/ve/4ZElEAGgwZgIxAL8vZfr6ekuOSjH4Y/u9WJloNNOpI0TG/KvH1TyXQyfVmRCrPShKmRwwORKl79n9HwIxAPas5ox0uqDbZUv15L2+5XwIKSVMnxJwxf7GPFgQG+IRIHQ/DXKBVXUL+Ec5C+gXAA==

Here is the public key, private key, and certificate I'm using to encrypt/decrypt the data. These keys are only used for my own testing so it is safe to share.

-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtTgxf4wvSC5sNnBk7q/I
J003Z9oHz9pnObZ/MfjPCHNYZoeqM0QaKAJ8RxkzEbyD5YJhb0NbTMJxzd706tJw
G66zIiyeSqr/UM/tqVNVTefYh4uLM3/wXyaYa6Hc+L+5Q165YFeOn0gnF2u1w2Cq
OX0ThQ5M1kMX0wPIpJSG1glbyGyjqvoqqDDrnfJHusQmHSB/M2ZUQOtwL7LEGD3K
g1mU/Fl2wusONLeT/YnklCHkiPvDHpxMNX3zuyj9b5vabIFYvfOWSiXdYWLg3ZNY
xpEnApe/qswkIOnd8Fv74yUgC2+KkkvX6yYmIgjO/UaNQ6uCun6IKvIRAjY/X6fm
5kEqOa98Bzj0EVVXasRtKILU05krJwU4oXivCSwPrV6lZf46bHbjfmcWBS/MMDdL
NiAoeh+GVCqUn2Lx1puaegNLPrTuv/cdntFoFXJDQSzn6dHb4Fv9Rbcnpp6gckdg
/Q1bSjtkdjGEf1cw2Go9+45FPf4mURdHNuRBeIv6ucdOldA4aqfFRi8ovtSmpKbk
x0vupZ3ubi5VffkWdyxWtL3RYHLUYgY8ikHHizgvuEkE8S+CRcPYuLP329u/LJDw
mamjB7gLIDjDN4QpHSLsbJ10xSkum5m8ehf9YlMh6bZV4dMtC0cqoeQ8k2xiNPo2
Ab2SUPDWjCdNehNtobrBOT8CAwEAAQ==
-----END PUBLIC KEY-----
-----BEGIN CERTIFICATE-----
MIIFZzCCA0+gAwIBAgIUIBYoz2BaYunsCR9zrwyMQd5n+Z0wDQYJKoZIhvcNAQEL
BQAwQzELMAkGA1UEBhMCQVUxDDAKBgNVBAgMA05TVzEPMA0GA1UEBwwGU3lkbmV5
MRUwEwYDVQQKDAxNWU9CIFB0eSBMdGQwHhcNMjAwNjE3MDIzNjQzWhcNMjIwNjE3
MDIzNjQzWjBDMQswCQYDVQQGEwJBVTEMMAoGA1UECAwDTlNXMQ8wDQYDVQQHDAZT
eWRuZXkxFTATBgNVBAoMDE1ZT0IgUHR5IEx0ZDCCAiIwDQYJKoZIhvcNAQEBBQAD
ggIPADCCAgoCggIBALU4MX+ML0gubDZwZO6vyCdNN2faB8/aZzm2fzH4zwhzWGaH
qjNEGigCfEcZMxG8g+WCYW9DW0zCcc3e9OrScBuusyIsnkqq/1DP7alTVU3n2IeL
izN/8F8mmGuh3Pi/uUNeuWBXjp9IJxdrtcNgqjl9E4UOTNZDF9MDyKSUhtYJW8hs
o6r6Kqgw653yR7rEJh0gfzNmVEDrcC+yxBg9yoNZlPxZdsLrDjS3k/2J5JQh5Ij7
wx6cTDV987so/W+b2myBWL3zlkol3WFi4N2TWMaRJwKXv6rMJCDp3fBb++MlIAtv
ipJL1+smJiIIzv1GjUOrgrp+iCryEQI2P1+n5uZBKjmvfAc49BFVV2rEbSiC1NOZ
KycFOKF4rwksD61epWX+Omx2435nFgUvzDA3SzYgKHofhlQqlJ9i8dabmnoDSz60
7r/3HZ7RaBVyQ0Es5+nR2+Bb/UW3J6aeoHJHYP0NW0o7ZHYxhH9XMNhqPfuORT3+
JlEXRzbkQXiL+rnHTpXQOGqnxUYvKL7UpqSm5MdL7qWd7m4uVX35FncsVrS90WBy
1GIGPIpBx4s4L7hJBPEvgkXD2Liz99vbvyyQ8Jmpowe4CyA4wzeEKR0i7GyddMUp
LpuZvHoX/WJTIem2VeHTLQtHKqHkPJNsYjT6NgG9klDw1ownTXoTbaG6wTk/AgMB
AAGjUzBRMB0GA1UdDgQWBBTXtKZu5k+mN7oEXTbBu9XB1drvPzAfBgNVHSMEGDAW
gBTXtKZu5k+mN7oEXTbBu9XB1drvPzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
DQEBCwUAA4ICAQBRqIdFT+vZVxrxE8P6oEE5gFfLop8EChRNpV+yRzixSxQofJLK
BC6jTNo1rjLJ/xBppTEHP9uM1vH5czJHCvdrCZqn2LkG0+ITzC76FG6uRzkN3zoY
Qk0XV7XMhDqU+IxlPgvQsM2x6w8MClsv4+6HhoCrW/z6bggUf1rMfAN0ae+VkFLg
dVBSJ0Kn4EQyQwLetWIa7qfA6bbCeKug6b0pels/hy54HIWXQEA3T+ZIDlon0xj5
8k/a1D3bMzB53f/mnZa9VFVtTGo5xYrm+S7Ex4l97iGTITxBf4/y8ZJvVMvVvKC2
JtehdEu5on0FODwmNFgVFC4ZaQ0/ro2JaFHbl9VzpNM46IiEwCN5W4SvhqC09/E/
SIW8j+FT/yKLukdVRpodoh+VkJ+Izkh6s2d1BSA6AMfLFSS+rAMX9dAQcRgPjclV
EITfcorQVJNDq2J+NrIk0SoA/TfQMMEARWJER2IFAMXiZ0IYEA2iB+ajey6yuSil
4/ZBR9ZSkb5kJuHv53r8804nuINNng42JPMr8bveuPS1slPzIT6S8jD7ykKNqtwc
mY5JxhX9SXPVX0uHk6lSz4NdTFEq/v9QXxZYuHZf2GnAGV0wXHuOwxWIY9tQKEiu
aMLZhYrCsGdRf0fmeZjrOeqDrnRjjvWG8yI5KTbNQzDV+5T5x4Vqr1hagw==
-----END CERTIFICATE-----
-----BEGIN PRIVATE KEY-----
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
IcJ4EqixWPnCt6/xnIr801futKtvdw==
-----END PRIVATE KEY-----
seebees commented 4 years ago

Thanks for the report! This was harder than it should have been. You will need 2 things

  1. Node.js v12.9 or greater
  2. Specify the oaepHash in the keyring construction
const keyring = new RawRsaKeyringNode({
  keyName,
  keyNamespace,
  rsaKey: { privateKey: privateKeyPEM },
  oaepHash: 'sha512'
});

I'm going to leave this open so I can work out what documentation should also be updated. The issue is that the keyrings must agree on the RSA padding.

iknowcss commented 4 years ago

Hey @seebees,

Yes! That solves my problem perfectly! And it's much faster than before. Cheers!

Regarding the documentation, I did not find any documentation around the oaepHash option for the RawRsaKeyringNode constructor. In fact, if there are JavaScript-specific API docs I could not find them anywhere. I did find this page which has a couple of simple examples, but there are no API-level docs for this library.

Also, how did you know that oaepHash should be set to 'sha512' as opposed to 'sha384' or one of the other valid options? I used the AWS Encryption SDK message format reference to decode the message header, body and footer, but I didn't see any information about what kind of OAEP hash algorithm to use (maybe I missed it).

Thanks again!

seebees commented 4 years ago

I did not find any documentation around the oaepHash

Yes that is something we are working on. It was a somewhat recent addition.

Additionally, the defaults should probably change. With all things in security, it is every upward. So having a default padding mode is sub-optimal.

In fact, if there are JavaScript-specific API docs

I'll take this as a request to produce docs from the typescript interfaces. :)

Also, how did you know that oaepHash should be set to 'sha512' as opposed to

I verified that I could indeed decrypt your message from the code linked in the blog post. Then I read through that code to find the options, (I used a little console.log magic... :( ) and with a little bit of intuition/memory.

The padding information is not stored in the ESDK message format, so that is just something you have to know :(

iknowcss commented 4 years ago

Great, well thank you again for your help! Let me know if there is anything I can assist with.

tarlingovo commented 4 years ago

@iknowcss, @seebees - thanks for posting this here.

Can I ask a couple of noob questions since I'm finding it difficult to piece together how to get this working in node from the docs.

I've run through the AWS example here and generated:

In your code, could you tell me where I get these values from:

Thanks for any pointers

iknowcss commented 4 years ago

Hi @tarlingovo! Sorry I didn't reply sooner.

Hope that helps!

tarlingovo commented 4 years ago

Hope that helps!

Thanks for taking the time to reply @iknowcss , it's really appreciated. I'm getting the same unencryptedDataKey has not been set error though, which I get however I configure the keyring. I've also tried swapping the keyName you suggested with the Key ID created when I added the public key to the Connect instance but no luck.

I've worked around this by repurposing the example code from the AWS Connect blog you linked to, swapping out the header deserialisation code with equivalent code from the '@aws-crypto/serialize' module. This works well enough, but I'd obviously just rather only the @aws-crypto module.

tarlingovo commented 4 years ago

Further to above - this does indeed work if you use the "Key ID" you get when you add your public key to your Connect instance. I think my issue was due to mismatches between my test data.

Here's the code I used to test if it's useful for anyone

// decrypt/index.ts

/* eslint-disable @typescript-eslint/no-unsafe-call */
import { decrypt as decryptLib, RawRsaKeyringNode } from '@aws-crypto/client-node';

type Decrypt = (input: string) => Promise<string>;

export const decryptFactory = (keyId: string, privateKey: string): Decrypt | null => {
  if (!keyId || !privateKey) {
    console.log('No decrypt parameters defined');
    return null;
  }

  const keyring = new RawRsaKeyringNode({
    keyName: keyId,
    keyNamespace: 'AmazonConnect',
    oaepHash: 'sha512',
    rsaKey: {
      privateKey,
    },
  });

  return async (encryptedText: string): Promise<string> => {
    const { plaintext } = await decryptLib(keyring, encryptedText, { encoding: 'base64' });
    return plaintext.toString('utf-8');
  };
};

export default decryptFactory;

// decrypt/index.spec.ts
import decryptFactory from '.';

describe('decrypt', () => {
  it('decrypts input to the expected string', async () => {
    const encryptedText =
      'AYADeCWMiXEmdOe6ZJgJXR3FKcQAXwABABVhd3MtY3J5cHRvLXB1YmxpYy1rZXkAREF1ZEVhd2F0ditqU3RBc2h2YzZDRDFMZmZvS0hwdlNsd2xJSVFBUEsvOWhidGs3SVZlMEVFRFNNSHZzaEg2T2dOZz09AAEADUFtYXpvbkNvbm5lY3QAJDdlMjAwZmI2LTUyNzktNDkzOS04YjkxLTMxZWY1YWNjNGJmYwEALjwuI23ohoECz3OSlYea6SdQTXQjtRmF01WFzhiFfjxpOs/pRURuxN+8Pb5Ztl8xz9ohQNzMQQ1OlY8wMWMpHkvCsFeiX3L2gp2POFJJPlgWt+fK0FKqqzR7N7thL6+ese2/FgXpwDyPcooVdcC59xOPNWdZ0BcfGLtYzlvU/4dgqfdfoTj6b/utieGdW2HTFJVm7zxQf54HaILiz0AhWJZHs9yGgfFW7vARoham3FWCjU4gJUa/tX3NIKTHLql5QjhJDY3J1HN7ghWjoatFfROQ7/qtxo//okg5pkcN/LYd66F4yCjSObtJ91VpZPBe2grM7MVn0dZTn4BTRhZliwIAAAAADAAAEADiU9BZgv9rMdkHXbLdTeTEH+qfG8HYsmsyVV9h/////wAAAAF8NqnlZPpid243SaQAAAAMxq2rZXOKoHxvWMM9D3GZezGDk3uxQCtV8Ma8eQBnMGUCMCbJtH7nwl5aUw4d17tlR5jp+ZFxZkUXREKJveX6iupIZdXpG1APoEMWOpkIR0xf5gIxAIfUneFWo08NkKCIgoRjN998eDK21lUcjYbWIHZPLtd9HRMl48Oh67GpGLi+k1IXhg==';
    const privateKey = `-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAtu0N2MM9KGXpb/ovCCHfbGEt3bfaXfnT61zqHv6o02A21dqS
QbR4VHZXvKSpy3JYjKq9hP+8fHl0ZgGw8r0C/yB+zMF0g9WXYgPmO369HH+3FJWY
zAq91VM1cPPCsiK7U2LHsaNgUF/808dXXJkUo5vOnWq2OMUJt2WMwRW4InJgIgqB
G9xxygfeZzEan9HYphZuVZeQbmQ8x79DyyP3hqbfWio8h9U4dY3ttyzUzfblhnzT
aAGIzWENVotwdpNc9cDezkNmntD/emK7mAYyvMzYryXTqlbU85aWVnjO/EMWXCoc
aOkWV+S7gZrasiaS2ep8Pc75FFULMThcmt8SWQIDAQABAoIBAG0oYI7tUt18kySj
ItWzgOPnybr5L3HM100oEP2V8aDRVWegGo7i5rtgM1L63+frwDOQ368x/IQ65d7J
O5JegB9GgOQmvSS+nKt8sDLommkbPWEnNBrufgGCue1BSPmMCETmOUwjtiHAZdrq
pZM4Njy7iTwepcaSrg2mp6vBDjRfEceUHIvYIfSH7yd23cWhfQj9Rp/MuOSqYWsI
kFtZiKF7vdKetuWrDxsBfRzKRionak267iiwnp6qjRUYGa61YnCBpeCC2+7SKIQW
ZP0xY0km79EX29XlKbvLMP9TbJWgh1rt3bgfUsTBiDVsEL3p7yRz0lgrNGFgkyzm
rv5CWdECgYEA4arZlOQxRqiq8cAVZFiiXqmxiP26aZJK0T2ZEahHH9jGsVln47EB
k9PVlP7ubx8odwG20mKPQN0di4OQvZEnlWUt/zQvqccG5KD7t6K59XF7EgoM0rUo
IEzBzDopfmtCpQ6S4HZr5McK8TRYgHmhqNC2Ua89Cdo+uDwlvbyc+P8CgYEAz4N8
M1wB52oW6SXnVgyJ0dfGHoWV0oMoR2WH+HQgB8/ICu1hKywELtfP6UrTQm++xU+P
R8XNJMc50hs2gscolYqGUNVGKjpvzQrK2P48tZEBpZnDHU2tcCytYKbLMPtTx45w
tqHIjuJ9fFU+VQX+MO0kM7H15qk0lspGrMQDXKcCgYBa7OH6Ue9gzvkBqylzYxsZ
FTqJPeJsQxuOL0mLicgOluOH9cQcQ+ekzoeSHx2m/GDrgm9NWZTqx6zt17bsCp5u
XXQpDbfeqmimmg6SMSjIMk8qN45vH7rpOMQlK5Ioc8NIihS4CD700RYhlbvLGDcA
tAeh5f+sBpma1fFG/8YntQKBgGMztlJPL9HTU7SNnDP0toYMovfAYMlMThsjnIbX
7OAPItCmfodG+ZDzNr+ON8RQ62AkEpj7JIDqhOe+fqayUuJ6YI+QBYT2frAE8sgd
ciRSd/kUssP1eG5hzVihJA38eCa0H4oPeXNlLxwbHDtFLClzlJTqBGYKict0U6kP
45ONAoGAZXutzl+QoZsddKC95uFX3r17cg2gW+Jy5grz6udpro1I+dA+FTf76+9a
G+v17Se+0GrRyhxrIL+L9/FCyaoJ0xy3/weHWSQqZFwxxniPB3nyjVpD3+kBmCsd
bUFa3fHAVLYmvA8QPhkUk070nAcAcB3rNFEdyWvF43U7ZTLATOY=
-----END RSA PRIVATE KEY-----`;
    const keyId = '7e200fb6-5279-4939-8b91-31ef5acc4bfc';
    const decrypt = decryptFactory(keyId, privateKey);
    const result = await decrypt(encryptedText);
    expect(result).toEqual('401234567890');
  });

  it('does not throw if no factory parameters passed', () => {
    expect(decryptFactory('', '')).toBeNull();
  });
});

//decrypt/utils.ts

import { NodeAlgorithmSuite } from '@aws-crypto/client-node';
import { deserializeFactory, HeaderInfo } from '@aws-crypto/serialize';

const toUtf8 = (input) =>
  Buffer.from(input.buffer, input.byteOffset, input.byteLength).toString('utf8');

const deserialize = deserializeFactory(toUtf8, NodeAlgorithmSuite);

/*
this function can be used to extract the keyId from an encrypted message for test data
*/

export const extractKeyId = (encryptedMessage: string): string => {
  const buffer = Buffer.from(encryptedMessage, 'base64');
  const headerInfo = deserialize.deserializeMessageHeader(buffer) as HeaderInfo;
  return headerInfo.messageHeader.encryptedDataKeys[0].providerInfo;
};
binli0114 commented 3 years ago

Thanks @iknowcss for raising the issue and thanks @seebees for the solution which solved the issue.

Grenter commented 3 years ago

@iknowcss had the exact same issue I have been facing so to find this was very helpful. I have since worked through it and made the necessary changes to use '@aws-crypto/client-node', however I get Malformed encrypted data key when trying to decrypt. I have even tried swapping out my privateKey, keyName and encrypted payload to use iknowcss values provided above but still get the same error. I have updated the above code slightly due to decrypt now being deprecated and to use buildDecrypt instead. Updated code as follows:



const { decrypt } = buildDecrypt(
  CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT
)

async Decrypt(base64CipherText, privateKeyPEM, keyName) {
  const keyring = new RawRsaKeyringNode({
    keyName,
    keyNamespace: 'AmazonConnect',
    rsaKey: { privateKey: privateKeyPEM },
    oaepHash: 'sha512'
  });

  const { plaintext, messageHeader } = await decrypt(keyring, base64CipherText, { encoding: 'base64' });

  return plaintext.toString('utf-8');
}
lavaleri commented 3 years ago

Hi @Grenter, I was unable to reproduce the error you are seeing using the code provided above and the privateKey/keyName/payload provided by iknowcss. I was able to successfully decrypt the payload.

Malformed encrypted data key is a strange error to see after previously seeing unencryptedDataKey has not been set. Can you confirm whether removing the oaepHash: ‘sha512’ line brings you back to the unencryptedDataKey has not been set error?

Also, can you reconfirm that you still see this error even after using the keyName, privateKeyPEM, and payload used by iknowcss above? Else, can you reproduce the error you are seeing with inputs that are safe to share here to so I can try to reproduce your issue?

mayank92 commented 3 years ago

Hi @lavaleri @Grenter Thanks for the post. But, unfortunately, I'm also running into the same issue today and getting the same error. Error: Malformed encrypted data key. Would be great @Grenter if you can share your results. If you are able to resolve this? Thanks for the post @iknowcss

gauthaman92 commented 1 year ago

Hi guys . Trying the same code and have the issue saying "not a supported format version" Please provide your suggestion for a fix