2

I have an encrypted file stored in a Google Cloud Storage bucket that was generated with the following command line:

gcloud kms encrypt --location=global --keyring=my-keyring --key=-my-key --plaintext-file=my-file --ciphertext-file=my-file.enc

I am now trying to decrypt such file in a Cloud Run service with the following code:

const kms = require('@google-cloud/kms');
const client = new kms.KeyManagementServiceClient();
const file = storage.bucket("my-bucket").file('my-file.enc');
const name = client.cryptoKeyPath( 'projectId', 'global', 'my-keyring', 'my-key' );
let encrypted = (await file.download())[0];
const [result] = await client.decrypt({name, encrypted });

I am getting the following error:

Error: Decryption failed: verify that 'name' refers to the correct CryptoKey.

Which, according to this, is misleading and should be considered as not being properly deciphered. I cannot shake the feeling that I am missing a base64 encode/decode somewhere but I don't seem to find the solution.

If I run the decryption from the command-line it works just fine.

Any help is very appreciated.

Thanks.

EDIT: Problem solved thanks to this awesome community. Here goes the steps to make this work, in case others face the same issue:

Encrypt the file using the following command line and upload it via the web UI.

gcloud kms encrypt --location=global --keyring=my-keyring --key=-my-key --plaintext-file=my-file --ciphertext-file=my-file.enc

Decrypt using the following code:

const kms = require('@google-cloud/kms');
const client = new kms.KeyManagementServiceClient();
const file = storage.bucket("my-bucket").file('my-file.enc');
const name = client.cryptoKeyPath( 'projectId', 'global', 'my-keyring', 'my-key' );
let encrypted = (await file.download())[0];
const ciphertext = encrypted .toString('base64');
const [result] = await client.decrypt({name, ciphertext});
console.log(Buffer.from(result.plaintext, 'base64').toString('utf8'))
Pedromlm
  • 89
  • 1
  • 9
  • try encoding your data in base64: "encrypted.toString('base64'));" And also, when passing to the function pass it as: "await client.decrypt ({name, ciphertext: encrypted});" (this last thing solved the error for me) – Mayeru Jul 29 '19 at 14:28
  • Mayeru, thank you for your help. I am now also convinced that the problem is exactly the key "ciphertext" on the JSON object that is passed as parameter to the decrypt method. That also worked for me (see below for full explanation). Thanks! – Pedromlm Jul 29 '19 at 14:31

1 Answers1

1

I spot a few things here:

  1. Assuming your command is correct, my-file-enc should be my-file.enc instead (dot vs dash)

  2. Verify that projectId is being set correctly. If you're populating this from an environment variable, console.log and make sure it matches the project in which you created the KMS key. gcloud defaults to a project (you can figure out which project by running gcloud config list and checking the core/project attribute). If you created the key in project foo, but your Cloud Run service is looking in project bar, it will fail.

  3. When using --ciphertext-file to write to a file, the data is not base64 encoded. However, you are creating a binary file. How are you uploading that binary string to Cloud Storage? The most probable culprit seems to be an encoding problem (ASCII vs UTF) which could cause the decryption to fail. Make sure you are writing and reading the file as binary.

  4. Looking at the Cloud KMS Nodejs documentation, it specifies that the ciphertext should be "exactly as returned from the encrypt call". The documentation says that the KMS response is a base64 encoded string, so you could try base64 encoding your data in your Cloud Run service before sending it to Cloud KMS for decryption:

    let encrypted = (await file.download())[0];
    let encryptedEncoded = encrypted.toString('base64');
    const [result] = await client.decrypt({name, encrypted});
    
  5. You may want to take a look at Berglas, which automates this process. There are really good examples for Cloud Run with node.

  6. For more patterns, check out Secrets in Serverless.

sethvargo
  • 26,739
  • 10
  • 86
  • 156
  • Seth, thank you for your support. I will address each of your points: 1. It was a typo on the question, it should be "." (the actual file is not even named that way, this was just an example); 2. The projectId checks out and it is the appropriate one. 3. I am uploading the file to Cloud Storage via the web UI (not sure how it is being written). 4. I am now trying this approach. Will come back with the results. – Pedromlm Jul 29 '19 at 14:27
  • Seth, just wanted to let you know that I managed to work it out. I had already tried with the "toString('base64') you suggested and had gotten no better results. However, after rerunning the tutorial by Google, the decrypt method takes, as parameter, a JSON object which I now suspect must have the keys "name" and "ciphertext" (please correct me if I am wrong) because that was the only thing ("ciphertext" instead of "encrypted" key) that I changed. Thank you so much for your help. – Pedromlm Jul 29 '19 at 14:30