Data Encryption and Decryption

In your Instnt JavaScript agent workflow configuration, you will configure the public part of a Public-Private RSA keypair. The private part of the keypair, which only you possess, is required to decrypt the data. The security of the keys is ensured by signing them with RSASSA-PKCS1-v1_5 SHA256 and encrypting them with RSA-OAEP for key wrapping and AES GCM for payload encryption. It's important to note that both of these processes require a minimum key length of 2048 bytes.

Generate Keys

To begin, we recommend generating a 4096-bit RSA key pair, aptly named 'private.pem.' This private key is further encrypted 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:

During this process, you will be prompted to enter and verify a passphrase for additional security.

Public Key

For creating a workflow, you'll need to extract the public key from the encrypted bundle generated earlier. This public key should be in Privacy Enhanced Mail (PEM) format, which is essentially a Base64 encoded DER certificate. PEM certificates are commonly used for web servers due to their readability with a simple text editor.

To extract the public key from 'private.pem' in PEM format, use the following command:

% openssl rsa -in private.pem -outform PEM -pubout -out public.pem

You'll be required to enter the passphrase for 'private.pem' during this process. The resulting 'public.pem' file should include headers and footers to be used for configuration.

% 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-----

Configuration and Workflow

To properly configure your workflow, copy and paste the complete 'public.pem' key, including the "BEGIN PUBLIC KEY'' and "END PUBLIC KEY" headers and footers, into the Encryption Management section located in the Security pane of Instnt's workflow builder. This step ensures that your data remains secure and accessible only to authorized parties.

Screen

Decrypting and Verifying the JWE Token

After a potential customer submits their information through your website's workflow fields, your business will receive onboarding decisions and assertion details in a secure JWT container format. To access this information, you must decrypt and validate the container. The JWT specification is supported by several programming languages, such as Python, Node.js, and Java.

To decrypt the JWE token, your business' private PKI key is used in the first step. In the second step, the JWS token signature enclosed in the container is verified as being issued by Instnt. This verification process 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-----

To resume the onboarding workflow after receiving the Instnt decision callback, you need to retrieve the user assertion from the Instnt Assertion API by using the instntid UUID reference provided in the Decision Callback payload. Instnt will provide a reference with a redirect URL for you to proceed with the next step in your business' onboarding process.

Before accessing the instntid, the instntjwt token must be decrypted. The instntid is then used as an input for the GetAssertion function, as described in the Analyzing the Assertion Response documentation.

Here are some decryption and verification examples written in Python and Node.js frameworks:

  • 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!");
    }
    }
    }

The token format and unwrapping process remain the same for both the assertion reference on the callback and the assertion itself, as explained in the Analyzing the Assertion Response documentation.