Saad Belkhou
Saad Belkhou

Reputation: 11

NodeJS decryption of bitnami sealed secrets

I want to reverse this encryption procedure in order to decrypt a sealed secret.
this is taken from the bitnami documentation.

Secret encryption

The secret is encrypted by AES-256-GCM with a randomly-generated single-use 32 byte session key.

The result of this operation will be called AES encrypted data in the next diagram, and the present step is the 1..

Session key encryption

The session key used by AES-256-GCM to encrypt the Secret is encapsulated with the controller's public key using RSA-OAEP with SHA256.

The OAEP input content, called label in the next diagram, differs depending on the sealed secret controller scope configuration. This algorithm is only used to encrypt the AES session key.

The result of the RSA-OAEP encryption is called RSA encrypted data in the next diagram, and the present step is the 2..

Sealed Secret storage

The final Sealed Secret data format is the following (where || is the concatenation operator): size of AES encrypted key (2 bytes) || RSA encrypted data || AES encrypted data

Diagram to summarize

                                Secret
                                                                   |
                                                                   │
                                                   K_s────────────►│
                                                    │              │
                                       K_pub───────►│              │
                                                    │              │ 1.
                                       label───────►│ 2.           │
                                                    │              │
                     ┌──────────────────────┬───────▼───────┬──────▼───────┐
Sealed Secret data = │size of AES encrypted │ RSA encrypted │ AES encrypted│
                     │key (2 bytes)         │ data          │ data         │
                     └──────────────────────┴───────────────┴──────────────┘

K_s = 256 bits single-use session key, used by AES-GCM
K_pub = Public key from the self-signed certificate, used by RSA-OAEP
label = Additional input for RSA-OAEP encryption.
        Content differs depending on the scope configuration:
         * Default config : label = Secret's namespace || Secret's name
         * Namespace-wide : label = Secret's namespace
         * Cluster-wide : label is empty

Decryption process

The decryption is simply the inversion of the encryption.

Size of AES encrypted key is read and used to separate RSA encrypted data and AES encrypted data properly.

Then the private key associated with the public key (see Session key encryption) is used with the label to decrypt the RSA encrypted data, effectively retrieving the AES session key.

To end this process, the AES encrypted data is decrypted using the AES session key, therefore unsealing the original Secret.

this is what i have done till now, but i am stuck:

import * as fs from 'fs';
import * as forge from 'node-forge';

// Load the private key from my local files
const privateKeyPEM = fs.readFileSync('priv.key', 'utf8');
const privateKey = forge.pki.privateKeyFromPem(privateKeyPEM);

// Sealed Secret data
const sealedSecretData = Buffer.from(
  'AgAdMSMYznQfAavqvsmqVnjnZYnaPHyrLoxsj6Qg5uoH0yWgkgea9ipMgl2CAXaatUxsz4Qx3ULM6GyRmOvTP47ETt0h3rALxK47lXdoDq3z9937/2rln2uMaGhCyyTQ0m7G3F0Rj3J5NRFnuuVhYZit/v6vrXHKqAHTuG0AxIA/nuN0o3nFN7+MmBs3RTXPII+6CYYmho3ldZwimyQKrBkbbZpRjFnmvv2ErGFQg9lz63QxzTqE5EPvrz0DIwGFUxJKKZ2fOucVcXavEkZJUflKFnK58d/ur90GPQp/N5Ivmqj+1Lf7L5ezqAP5WpmK0M2q69D6RG+OUoOgpYLNQJybL4nxqgv4lFu0eXHxBwqjmNUOqkFDAd5APS1CDaFNjcw5dJ009jqeUN+4AxM2WtfzjjQz6rf5M5UmGMDCc26mjx3fOvi8AAIvEJ6B/hJcRwnC0zqsK0apJ00VykRyHsOTK8G6/eFy8wFnvx4neBCNKgQAMMq0F8A5ekWVZg7sxdeUucc2KXHc53c/p+tOcf0eMBCwefM7ijcoc85x+0wr+wigJ71BakjCGZfQDlnnfOGf9ayp4EhpiPfUdNQ8k1A3G45dE8wJdrUtvn4Qoootevhb9acWM3rKjFstDj3FB8epCct85Fnu5y8QGqWPfUTiprSNWAc+1zejXtZULSWal2cf66kniG+hjjAeEU8l2unncQj+0mf/',
  'base64'
);

// Parse the Sealed Secret data
const aesKeySize = sealedSecretData.readUIntBE(0,2);
const rsaEncryptedData = sealedSecretData.slice(2, 2 + aesKeySize);
const aesEncryptedData = sealedSecretData.slice(2 + aesKeySize , sealedSecretData.length - 1);

// the label as specified in the docs above
const label = Buffer.from('kube-systemsealed-secrets-keyfc5ht', 'utf8'); 

// Decrypt RSA encrypted data to get the AES session key
const rsaEncryptedBuffer = forge.util.createBuffer(rsaEncryptedData.toString('binary'));
const rsaDecryptedBytes = privateKey.decrypt(rsaEncryptedBuffer.getBytes(), 'RSA-OAEP', {
  md: forge.md.sha256.create(),
  label,
});

const aesSessionKey = forge.util.createBuffer(rsaDecryptedBytes).getBytes();

// Decrypt AES encrypted data
const aesDecipher = forge.cipher.createDecipher('AES-GCM', forge.util.hexToBytes(aesSessionKey));
aesDecipher.start({ iv: aesEncryptedData.slice(0, 16) });
aesDecipher.update(forge.util.createBuffer(aesEncryptedData.slice(16)));
aesDecipher.finish();

const decryptedSecretData = aesDecipher.output.getBytes();

console.log(decryptedSecretData);

i get an error as follows: Error: Invalid RSAES-OAEP padding.

Upvotes: 0

Views: 144

Answers (0)

Related Questions