Data Encryption and Decryption

Overview

You'll be setting up the public portion of a Public-Private RSA keypair in your Instnt javascript agent workflow configuration. You alone, being in possession of the private portion of the keypair, can decrypt this data. These keys are signed via RSASSA-PKCS1-v1_5 SHA256 and encrypted by RSA-OAEP for key wrapping and AES GCM for payload encryption. Both of these processes require a minimum key length of 2048 bytes.

Generate Keys

First, generate a 4096-bit RSA key pair private.pem that encrypts a passphrase using the AES256 cipher:

% openssl genrsa -aes256 -out private.pem 4096
Generating RSA private key, 4096 bit long modulus
.............................................................
..............................................................++
..........................................................................................++
e is 65537 (0x10001)
Enter pass phrase for private.pem:
Verifying - Enter pass phrase for private.pem:

Public Key

The public key required when creating a wortkflow must be extracted from the encrypted bundle created above. This can be extracted into a Privacy Enhanced Mail (PEM) format.

PEM is a Base64 encoded DER certificate. PEM certificates are frequently used for web servers as they can easily be translated into readable data using a simple text editor.

Generally when a PEM encoded file is opened in a text editor, it contains very distinct headers and footers as shown below:

% openssl rsa -in private.pem -outform PEM -pubout -out public.pem
Enter pass phrase for private.pem:
writing RSA key
% cat public.pem
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAySvoxZG9dKFjV/7Jfg27
cZKdUr7sfeeziwnZXMGLkvsRZldxNdWPMkgr/UvgQuov5TlTXG8wddN9YJGDsFQt
F9xzEgC9uAZ8LZkP4nyRm41NyHSwaX+8ANy1X0Xngsqmi3EqF6mCfj733lGq24uH
GwfZ3NGtzFeXthTSTHvOuFpnVX2ci2l4XEEU0R/qowWJ76aJAgEpquMEazzY6Vff
Sxyw9rBDS5YNbL/i6JlTNUElVY2A+CkYZ/dILe+iod/lXyiB6Y0nKfqpExK9t3RJ
gxXcxOcquABIuMMAtKPe/jQElHTX5xkCPdkPwmabcTj+eOUY4EdCf0T9nfB7M6Fg
TDvFAvm0xYo/T4pJ22kO1I6x5dT+u12ZNueDQfLVlKFi4381/9f/vDCn0hi7a1EL
CxiQqGnK6XBz06oB+zfYdButSArn14K81cXr/ETAxgN9SgjjWSecaFfktcI4oQdD
7FaCQwZX6C8gMUMQX/j2Xn+n9pPXBvyCfgAa4fgBRzU4SAIH4StVrz+lv6b8zXI0
taDKGjwDNrcjJKzBGYs3RvhzSuDwKqI037/k1+nx/J53SZ1uqRx+OrqfSK2FXqVn
xzE6iuRo8cCFDVDVVCku6ZO/n9qAH6InGtRu1S4gRM/0txLa8whZKzv+/vdD1QhA
5M8HKZ7bKCLkRxTlZasv6rsCAwEAAQ==
-----END PUBLIC KEY-----
The entirety of the public.pem key generated above including the "BEGIN PUBLIC KEY" and "END PUBLIC KEY" header and footer must be placed in the Encryption Management section in the Security pane of Instnt's workflow builder as shown below:
security.png

Decrypting and Verifying the JWE Token

Once a potential customer fills out the workflow fields listed on your website and submits their information, your business will receive onboarding decisions and assertion details in a cryptographically secure JWT container format which they must decrypt and validate. The JWT specification has support in multiple programming languages including but not limited to: Python, Node.js, and Java. 

The JWE token is first decrypted using your business' private PKI key. In the second step the enclosed JWS token signature is verified as having being issued by Instnt. This verification uses the public portion of the Instnt signing key which is shared with you:

  • -----BEGIN PUBLIC KEY-----
    MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvZmtK56bWzsQiCnXysb9
    qCRgIW0NSVtZUmlOyVQSYndsM01fExznujGn4I9mHVjuecnObQckklzCeq3VbfIr
    SmcCUyNegoiTzBUjJ9XuIXjnDUTeoCzTtDuOLdgespYQ/mAnO+EDa2tKiZiSUkNZ
    tZ40XyHgd5w9L58FhzSX64LjduWYpiDXLAUQtWFOixkrWjAV4/WSqZdEfKf3kkrz
    P7cIfuJwkg8EmcQwJ8uMbUFyvgeoPghdhcpOZV8uIf91rrhWaJMxQqXb7RafoFIM
    dlvwE2GAgyMHxHcGKlPq5AJucaiuYzSzp8rSyTj5+Z841jmqpxk6WFuV6T9yk5a7
    8KPZz8mwjkEvZZQN/0sbPEH9skH+VK+DrTsIOQkUmu/dCcS78M///TnfCWNGvsxh
    7N6xz9PAVjXuegj2tqLUuhnGiZKWovQP+teSnEeZT17T+Fg1Yt4uhHNNCTlxESX6
    BNH1Iq1WR99SScVjCrNQ0G9VewcGH1Ym7qFrLzihYAMmMF4orGO9slrD2kZN/FPs
    vd4Xe3qQuzC7McFiFCb8xMvsaVi+u2Ogy2Q775hXWpwcrcKgm9Iky7vA2k/qbRLe
    CQoHHgWOZiypX1ZfBYV98dI3kMfsh6TYOTmJXw3P4wij7DsKcKhCPLDnAIKqBkyz
    Q1TldZKTWJJxtqDC525oCVECAwEAAQ==
    -----END PUBLIC KEY-----
  • -----BEGIN PUBLIC KEY-----
    MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyxnDuCjaGcK0QLWx5m4d
    cNfalWJp2shGHV32NReGxqw1JM+toXDK6cqvmUomZ9DG6bVembw2hDKPTajsQwJO
    nrM6D0KfNiAxi2Mm01nQ7a9+39/l0uSxBMHLVk9e9w3GHiCz/E7cR1b8myrnghoW
    lMKUmMhTOCDANglf3LtIv4Tj13V8Si/Rj+AoBU/st8g6e6OnpZbqjOfSb9VS+3PI
    TrM93drjzW+ZE2L/tyF1pM6f+OavBDUtKwAqguXB7RU2JMp8hNw7ehSoqx1vFmqL
    q6gKwURfsjIwh2dVNMm+3Icr5ZalH188/0iwPlu5zC9bknTG1K8gPVHs8LlFRAxJ
    asIlTOrGSk4v5Qaf4wskqCBlA5aFvoQzDfJhMgnN0x9eHocY6MmztEMViOJl9XyO
    +aEdAfaVqrHstBiT/r6FBe5/mCR4I+fl+GdO6AxCOIJ0ZKUTkbWPVy6jjOvWFm7z
    INfqZas9IQ7rvQv0a/hvs03pewToEwVycbgdlfMJ2B62jR+EDFYpzJnOwvuTLcZE
    SX2tlf1bGPiuoJD+IJKvAb9Mdf90ZTMuldiEdLnPcjC13ITxYrPfuENfYLs6fVMl
    lxn1qUVnyngqzUSKu6c6D1WVkSB2modtyUNbAMEEyf3UY7gaPL5TciRktltK2Zi7
    twXuhzM/UCTR2XXNaFmdMyECAwEAAQ==
    -----END PUBLIC KEY-----

In order to resume the onboarding workflow after the Instnt decision callback, you must retrieve the user assertion from the Instnt Assertion API using the instntid UUID reference in the Decision Callback payload. Instnt provides a reference with this redirect for you to move on with the next step in your business' onboarding process.

The instntjwt token must be decrypted first to access the instntid which is used as an input to the GetAssertion function as described in Analyzing the Assertion Response.

The following decryption and verification examples are written within the frameworks of Python and Node.js:

  • from authlib.jose import JsonWebEncryption
    from authlib.jose import JsonWebSignature
    from authlib.jose import JWE_ALGORITHMS

    # JWE token
    JWE = 'eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.Tst5foFWOBfhGiKSBse3R3...'

    # Read customer private key
    with open('private.pem', 'r') as f:
    private_key = f.read()

    # Decrypt JWE
    jwe = JsonWebEncryption(algorithms=JWE_ALGORITHMS)
    jwe_decrypted = jwe.deserialize_compact(JWE, private_key)

    # Read Instnt public key
    with open('instnt_kms_public.pem', 'r') as file:
    instnt_kms_public_pem = file.read()

    # Validate JWS
    jws = JsonWebSignature(algorithms=['RS256'])
    assertion = jws.deserialize_compact(jwe_decrypted['payload'], instnt_kms_public_pem)
    json.loads(assertion['payload'].decode('utf-8'))

    # {'iss': 'https://instnt.org', 'aud': 'https://acmebank.org', ..., 'instntid': '90205512-586d-4637-86cd-b63b582e480c'}
  • var jose = require('node-jose');
    var fs = require('fs');

    // JWE token
    var JWE = 'eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.Tst5foFWOBfhGiKSBse3R3...'

    // Read customer private key
    var private_pem = fs.readFileSync('private.pem', 'utf8');
    var private_key;
    jose.JWK.asKey(private_pem, 'pem').
    then(function(result) {
    private_key = result;
    });

    // Decrypt JWE
    var jwe_decrypted;
    jose.JWE.createDecrypt(private_key).
    decrypt(jwe).
    then(function(result) {
    jwe_decrypted = result;
    });

    // Read Instnt public key
    var instnt_kms_public_pem = fs.readFileSync('instnt_kms_public.pem', 'utf8');
    var instnt_public_key;
    jose.JWK.asKey(instnt_kms_public_pem, 'pem').
    then(function(result) {
    instnt_public_key = result;
    });

    // Validate JWS
    var assertion;
    jose.JWS.createVerify(instnt_public_key).
    verify(jws).
    then(function(result) {
    assertion = result;
    });

    assertion.payload.toString()
    // {'iss': 'https://instnt.org', 'aud': 'https://acmebank.org', ..., 'instntid': '90205512-586d-4637-86cd-b63b582e480c'}
  • import com.nimbusds.jose.crypto.RSADecrypter;
    import com.nimbusds.jose.crypto.RSASSAVerifier;
    import com.nimbusds.jose.JWSVerifier;
    import com.nimbusds.jose.jwk.JWK;
    import com.nimbusds.jose.jwk.RSAKey;
    import com.nimbusds.jwt.EncryptedJWT;
    import com.nimbusds.jwt.SignedJWT;
    import java.nio.charset.StandardCharsets;
    import java.nio.file.Files;
    import java.nio.file.Paths;

    public class JWTDecryptVerify
    {
    public static void main(String[] args) throws Exception
    {
    String jwe = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ....";

    // Customer private encryption key
    String private_pem = new String(Files.readAllBytes(Paths.get("private.pem")),
    StandardCharsets.UTF_8);
    JWK private_key = JWK.parseFromPEMEncodedObjects(private_pem);
    RSADecrypter decrypter = new RSADecrypter((RSAKey) private_key);

    // Decrypt
    EncryptedJWT jweObject = EncryptedJWT.parse(jwe);
    jweObject.decrypt(decrypter);

    // Instnt public signing key
    String instnt_kms_public_pem = new String(Files.readAllBytes(Paths.get("instnt_kms_public.pem")),
    StandardCharsets.UTF_8);
    JWK instnt_kms_public_key = JWK.parseFromPEMEncodedObjects(instnt_kms_public_pem);
    JWSVerifier verifier = new RSASSAVerifier((RSAKey) instnt_kms_public_key);

    // Verify
    SignedJWT signedJWT = jweObject.getPayload().toSignedJWT();
    if (signedJWT.verify(verifier)) {
    System.out.println(signedJWT.getJWTClaimsSet());
    } else {
    System.err.println("Not verified!");
    }
    }
    }

This same token format and unwrapping process is used for both the assertion reference on the callback as well as the assertion itself as described in Analyzing the Assertion Response.

Was this article helpful?

0 out of 0 found this helpful

Have more questions? Submit a request