Closed iknowcss closed 2 years ago
Thanks for the report! This was harder than it should have been. You will need 2 things
oaepHash
in the keyring constructionconst 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.
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!
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 :(
Great, well thank you again for your help! Let me know if there is anything I can assist with.
@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:
privateKeyPEM
- would this be the content of foo.private.key
as-is, or transformed in some way, or something elsekeyName
- would this be foo
in my example?keyNamespace
always 'AmazonConnect'
for messages encrypted by Connect?Thanks for any pointers
Hi @tarlingovo! Sorry I didn't reply sooner.
privateKeyPEM
- yes, you're right. This should be a string representation of foo.private.key
. i.e. '-----BEGIN PRIVATE KEY-----\nMIIJQQIBADANBgkqhkiG9w0BA...etc...kRynz7N\n-----END PRIVATE KEY-----'
keyName
- from my experience, this value is always 4cdb826a-24c9-443d-b78b-327c817073ce
. I don't know why, but it seems to be the value that Connect always uses when encrypting a message. I obtained this value by manually debugging the execution of this library to see what value is encoded in the encrypted message.keyNamespace
- Yes, it is always 'AmazonConnect'
Hope that helps!
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.
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;
};
Thanks @iknowcss for raising the issue and thanks @seebees for the solution which solved the issue.
@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');
}
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?
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
Hi guys . Trying the same code and have the issue saying "not a supported format version" Please provide your suggestion for a fix
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: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:When I try to decrypt with
newDecrypt
, I get the following error stack trace: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: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
'sencrypt
method. The 1st one fails, while the 2nd one works :thinking:Connect payload The decrypted payload is
1236547891321456
.Payload encrypted with @aws-crypto/client-node
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.