This document describes the Crypto library available in the JavaScript environment for app serivices.
| Object | Crypto |
| Functions |
hash hmac cipher ecdh ecdsa |
| Object |
Hash |
| Functions |
update final |
| Object |
Hmac |
| Functions |
update final |
| Object |
Cipher |
| Members |
iv getIv crypt close |
| Object |
Ecdh |
| Members |
public secret close |
| Object |
ECDSA |
| Members |
public private sign verify close |
| Examples |
Encrypting and signing a JS object |
Creates a Hash object that can be used for the calculation of cryptographic hashes.
The objects allocates persistent memory in the JavaScript Runtime until the final function is called.
| string algorithm | The hash algorithm to be used. Supported values are "SHA224", "SHA256", "SHA384", "SHA512", "SHA512_224", "SHA512_256", "SHA1", "MD5", "MD4". |
| Hash | An object that can be used for the hash calculation. |
var digest = Crypto.hash("SHA256").update("data1").update("data2").final();
Creates a Hmac object that can be used for the calculation of a hash-based message authentication code.
The objects allocates persistent memory in the JavaScript Runtime until the final function is called.
| string algorithm | The hash algorithm to be used. Supported values are "SHA224", "SHA256", "SHA384", "SHA512", "SHA512_224", "SHA512_256", "SHA1", "MD5", "MD4". |
| string/Uint8Array key | The password or key that shall be used for the calculation. |
| Hmac | An object that can be used for the HMAC calculation. |
var mac = Crypto.hmac("SHA256","password").update("data1").update("data2").final();
Creates a Cipher object that can be used for the encryption / decryption.
The objects allocates persistent memory in the JavaScript Runtime until close or crypt with last=true is called.
Note: Cryptography is non-trivial. It's easy to make small mistakes on application level that break security completely. Don't just use the library functions but make sure you have a good understanding how to use the specific algorithm and mode.
| string algorithm | The cryptographic algorithm to be used. Supported values are "AES", "RC4", "DES". |
| string mode | The mode of operation to be used. Supported modes for "AES" are "ECB", "CTR", "CBC", "CTS", "CFB", "GCM". null defaults to "ECB". Supported modes for "DES" are "CBC". Use null for "RC4". |
| string/Uint8Array key |
The key for encryption/decryption. For "AES" a key of 16 bytes (AES-128), 24 bytes (AES-192) or 32 bytes (AES-256) is needed. The function accepts both hexstrings and binary keys given as a Uint8Array. For "DES" a key of 8 bytes (64 bit) is needed. The function accepts both hexstrings and binary keys given as a Uint8Array. For "RC4" a key of arbitrary length can be used. It can be specified as a string (password) or as a binary key given as a Uint8Array. Important: Never reuse the same key for "RC4". As RC4 is a stream cipher, the key needs to be globally unique to maintain security. Important: Never reuse the same key/iv combination for "AES" in "CTR" mode. As AES-CTR is a stream cipher, the key/iv combination needs to be globally unique to maintain security. Important: The same applies to "AES" in "GCM" mode, which is built on CTR. Reusing a key/iv combination there does not only reveal the plaintext, it also allows an attacker to forge authentication tags for that key. |
| bool encrypt |
true for encryption (default). false for decryption Note: For stream ciphers like RC4 or AES-CTR there is no difference between encryption and decryption, so both values give the same result. |
| cipher | An object that can be used for performing the encryption or decryption. |
var ciphertext = Crypto.cipher("RC4", null, "password").crypt("secretdata");
var decrypted = Crypto.cipher("RC4", null, "password").crypt(ciphertext);
Creates an Ecdh object that can be used for Diffie-Hellman key agreement using elliptic curves.
The supported curve is Secp256r1 also known as P-256.
The objects allocates persistent memory in the JavaScript Runtime until close or secret is called.
| Ecdh | An object that can be used for performing the ECDH key agreement. |
var ecdh = Crypto.ecdh();
var localPublicKey = ecdh.public();
var remotePublicKey = "aec36e41791fabfc2ddd3223a8ef59a658a9e30cd92a5d241bbf4c18684a4a61c9aa0d1d9d3181d3022d2f64c7663000915622d4e670cce86737bb00cab5de91";
var sharedSecret = ecdh.secret(remotePublicKey);
Creates an ECDSA object that can be used for the Elliptic Curve Digital Signature Algorithm.
The objects allocates persistent memory in the JavaScript Runtime until close is called.
| string algorithm | The signature algorithm to be used. Supported value is "ES256". |
| string/Uint8Array privateKey |
Optional. The private key to use for signing. Can be provided as a hexstring or a Uint8Array. If omitted, signing is not possible. |
| string/Uint8Array publicKey |
Optional. The public key to use for verification. Can be provided as a hexstring or a Uint8Array. If omitted, verification is not possible. |
If both privateKey and publicKey are omitted (or null), a new key pair will be generated. You can then retrieve them using the private() and public() methods.
| ECDSA | An object that can be used for signing and verification. |
// Generate a new key pair
var ecdsa = Crypto.ecdsa("ES256", null, null);
var newPrivateKey = ecdsa.private(true);
var newPublicKey = ecdsa.public(true);
ecdsa.close();
// Create an object for verification only
var ecdsaVerify = Crypto.ecdsa("ES256", null, newPublicKey);
// Create an object for signing only
var ecdsaSign = Crypto.ecdsa("ES256", newPrivateKey, null);
| string/Uint8Array key | The input data. The function accepts both strings and binary data as Uint8Array. |
| Hash | A reference to the object itself. Useful for method chaining. |
| bool binary | Optional, defaulting to false. Specifies if the function should return the hash value as a hexstring or as a binary Uint8Array. |
| string/Uint8Array | The calculated hash value as a hexstring or Uint8Array, depending on the binary parameter. |
Calling this function will delete the object and free all associated memory in the JavaScript Runtime.
var digestHexString = Crypto.hash("SHA256").update("data1").update("data2").final();
var digestBinary = Crypto.hash("SHA256").update("data1").update("data2").final(true);
| string/Uint8Array key | The input data. The function accepts both strings and binary data as Uint8Array. |
| Hmac | A reference to the object itself. Useful for method chaining. |
| bool binary | Optional, defaulting to false. Specifies if the function should return the hash value as a hexstring or as a binary Uint8Array. |
| string/Uint8Array | The calculated hash value as a hexstring or Uint8Array, depending on the binary parameter. |
Calling this function will delete the object and free all associated memory in the JavaScript Runtime.
var hacHexString = Crypto.hmac("SHA256","password").update("data1").update("data2").final();
var hmacBinary = Crypto.hmac("SHA256","password").update("data1").update("data2").final(true);
Sets an explicit initialization vector. If the function is never called, an IV consisting of null bytes is used.
The following modes of operation use an initialization vector: "CTR", "CFB", "CBC", "CTS", "GCM".
"AES" uses an initialization vector of 16 bytes, except in "GCM" mode, where it is 12 bytes
"DES" uses an initialization vecrot of 8 bytes
| string/Uint8Array iv | The initialzation vector to be used from the next crypt operation. The function accepts both hexstrings and binary data as Uint8Array. |
| cipher | A reference to the object itself. Useful for method chaining. |
Returns the current IV.
| bool binary | Optional, defaulting to false. Specifies if the function should return a hexstring or a binary Uint8Array. |
| string/Uint8Array | The current IV value as a hexstring or Uint8Array, depending on the binary parameter. |
| string/Uint8Array/object data |
The data to be encrypted/decrypted. For encryption the functions accepts strings or binary data as Uint8Array. For decryption the function accepts hexstrings or binary data as Uint8Array. Depending on the algorithm and mode of operation there are restrictions on the data size that can be processed. "RC4" allows arbitrary data sizes. "AES" in "CTR", "CTS" or "GCM" mode allow arbitary data sizes. All other modes of "AES" expect multiples of 16 bytes "DES" expects multiples of 8 bytes "AES" in "GCM" mode expects an object instead, with the following members: data - the data to be encrypted/decrypted, encoded as described above. Can be omitted to authenticate additionalData without encrypting anything. additionalData - optional. Data that is authenticated by the tag but not encrypted, for example a message header the receiver needs to read before decrypting. The function accepts strings or binary data as Uint8Array, for encryption and decryption alike. tag - the 16 byte authentication tag. Required for decryption, ignored for encryption. The function accepts hexstrings or binary data as Uint8Array. |
| bool binary |
Optional, defaulting to false. If false, the function will return a hexstring for encryption and a string for decryption. Otherwise the function will return binary data as a Uint8Array. In "GCM" mode this applies to every member of the returned object: data as described above, additionalData as a string and tag as a hexstring. |
| bool last |
Optional, defaulting to true. If true, the Cipher object will be deleted after the operation, along with all associated data in the JavaScript Runtime. if false, the object can be reused for encrypting / decrypting additional chunks of data. "GCM" authenticates the message as a whole and cannot be used incrementally, so false is not allowed there. |
| string/Uint8Array | The encrypted/decrypted data a string/hexstring or Uint8Array, depending on the binary parameter. |
| object |
For "AES" in "GCM" mode an object with the members data, additionalData and tag, encoded according to the binary parameter. data and additionalData are null if they were empty. The object has the same shape the function accepts, so the result of an encryption can be handed to crypt of a decrypting Cipher unchanged. null if the authentication failed on decryption, or if neither data nor additionalData was given. |
In "GCM" mode the tag is verified on decryption. If it does not match, crypt returns null and the decrypted data is discarded - it is never handed out. Always check for null. Data that fails authentication has been modified and must not be used.
var key = "73757065727375706572736563726574";
var iv = "00000000000000000000000000000000";
var plaintext = "This is the secret text to be encrypted";
// encryption using AES-128 in CTR mode
var ciphertext = Crypto.cipher("AES", "CTR", key, true).iv(iv).crypt(plaintext);
// decryption using AES-128 in CTR mode
var decrypted = Crypto.cipher("AES", "CTR", key, false).iv(iv).crypt(ciphertext);
// now decrypted contains the plain text again
var key = "000102030405060708090a0b0c0d0e0f";
var iv = "0123456789ab0123456789ab";
var plaintext = "This is the secret plaintext";
var header = "message-header";
// encryption using AES-128 in GCM mode
var r = Crypto.cipher("AES", "GCM", key, true)
.iv(iv)
.crypt({ data: plaintext, additionalData: header });
// now r contains the encrypted data, the additional data and the authentication tag
// {
// data: "5da70146824c2370d9ba86dba5533d1f1d11e1906c37d0fddf54a2f6",
// additionalData: "message-header",
// tag: "8ee868b4b1e8d5011dc3cface146c87d"
// }
// decryption using AES-128 in GCM mode
var d = Crypto.cipher("AES", "GCM", key, false)
.iv(iv)
.crypt({ data: r.data, additionalData: header, tag: r.tag });
// now d contains the plain text again, along with the verified additional data and tag
// {
// data: "This is the secret plaintext",
// additionalData: "message-header",
// tag: "8ee868b4b1e8d5011dc3cface146c87d"
// }
// d is null if the ciphertext, the additional data or the tag was modified
log(d && d.data == plaintext ? "OK" : "NOK"); // OK
Returns the local 64 byte public key for the ECDHE key agreement. The public key is sent to the remote party.
| bool binary | Optional, defaulting to false. Specifies if the function should return a hexstring or a binary Uint8Array. |
| string/Uint8Array | The local public key as a hexstring or Uint8Array, depending on the binary parameter. |
Calculates a 32 byte shared secret using the remote public key.
| string/Uint8Array remotePublicKey |
The public key of the remote party. Must be 64 bytes of data. The functions accepts both hexstrings or binary data as Uint8Array. |
| bool binary |
Optional, defaulting to false. If true, the function will return a Uint8Array. Otherwise the function will return a hexstring. |
| string/Uint8Array | The 32 byte shared secret as a hexstring or Uint8Array, depending on the binary parameter. |
// This example illustrates how two parties (A and B) can calculate a shared secret.
var a = Crypto.ecdh();
var b = Crypto.ecdh();
// A and B calculate their public key and send it to each other
var publicA = a.public();
var publicB = b.public();
// A and B calculate the same shared secret using the public key of the remote party
var secretA = a.secret(publicB);
var secretB = b.secret(publicA);
log(secretA == secretB); // true
Returns the public key from the ECDSA object. This is useful when a key pair has been generated automatically.
| bool binary | Optional, defaulting to false. Specifies if the function should return a hexstring or a binary Uint8Array. |
| string/Uint8Array | The public key as a hexstring or Uint8Array, depending on the binary parameter. |
Returns the private key from the ECDSA object. This is useful when a key pair has been generated automatically.
| bool binary | Optional, defaulting to false. Specifies if the function should return a hexstring or a binary Uint8Array. |
| string/Uint8Array | The private key as a hexstring or Uint8Array, depending on the binary parameter. |
// Generate a new key pair and retrieve the keys
var ecdsa = Crypto.ecdsa("ES256", null, null);
var privateKey = ecdsa.private(true); // get as Uint8Array
var publicKey = ecdsa.public(); // get as hex string
ecdsa.close();
Signs a chunk of data using the private key.
| string/Uint8Array data | The data to be signed. The function accepts strings or binary data as Uint8Array. |
| bool binary |
Optional, defaulting to false. If true, the function will return the signature as a Uint8Array. Otherwise the function will return a hexstring. |
| string/Uint8Array | The signature as a hexstring or Uint8Array, depending on the binary parameter. |
// Example of creating a JWT signature
var header = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9";
var payload = "eyJpc3MiOiJqd3QtdG9vbCIsInN1YiI6InVzZXIxMjMiLCJpYXQiOjE3NTEzNjY0MjksImV4cCI6MTc1MTM3MDAyOSwiZGF0YSI6IkhlbGxvLCBKV1QhIn0";
var textToSign = Encoding.stringToBin(header + "." + payload);
var privateKey = Encoding.hexToBin("fc7682132...4ce"); // Your private key
var ecdsa = Crypto.ecdsa("ES256", privateKey, null);
var signature = ecdsa.sign(textToSign, true); // Get signature as Uint8Array
var jwt_signature = Encoding.binToBase64Url(signature);
ecdsa.close();
var jwt = header + "." + payload + "." + jwt_signature;
Verifies a signature against a chunk of data using the public key.
| string/Uint8Array signature | The signature to verify. The function accepts hexstrings or binary data as Uint8Array. |
| string/Uint8Array data | The data that was originally signed. The function accepts strings or binary data as Uint8Array. |
| bool | Returns true if the signature is valid, otherwise false. |
// Example of verifying a JWT
var jwt = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJqd3QtdG9vbCIsInN1YiI6InVzZXIxMjMiLCJpYXQiOjE3NTExMjQ2NDUsImV4cCI6MTc1MTEyODI0NSwiZGF0YSI6IkhlbGxvLCBKV1QhIn0.JczPUqYLV0Cr3LzI5hnVrUjVgjO_-XeMrIlSxuXQGRn9t7s6FBXtNkO-8dl1YvSJGg4xC7gH_7smoan0bKPKAA";
var parts = jwt.split('.');
var text = Encoding.stringToBin(parts[0] + "." + parts[1]);
var signature = Encoding.base64UrlToBin(parts[2]);
var publicKey = Encoding.hexToBin("04d1dc4c38ad561530d4c53793ed6efba223cb1fe0c171104c2b03621cdd954c66bc2cfabcba8b4fb126c45973c47ef783fb5f3a39ac3a145e105911d062ca6197");
var ecdsa = Crypto.ecdsa("ES256", null, publicKey);
var valid = ecdsa.verify(signature, text); // returns true if valid
ecdsa.close();
log("JWT Signature is valid: " + valid);
function encryptObject(obj, passphrase) {
// serialize the object
var plaintext = JSON.stringify(obj);
// calculate HMAC
var hmac = Crypto.hmac("SHA256", passphrase).update(plaintext).final();
// create random 128-Bit IV
var iv = Random.bytes(16);
// create 256-Bit encryption key from passphrase by hashing it using SHA256
var key = Crypto.hash("SHA256").update(passphrase).final();
// encrypt
var ciphertext = Crypto.cipher("AES", "CTR", key, true).iv(iv).crypt(plaintext);
return {
iv: iv,
data: ciphertext,
hmac: hmac
};
}
function decryptObject(obj, passphrase) {
if (!obj || !obj.iv || !obj.data || !obj.hmac) return null;
// create 256-Bit encryption key from passphrase by hashing it using SHA256
var key = Crypto.hash("SHA256").update(passphrase).final();
// decrypt
var plaintext = Crypto.cipher("AES", "CTR", key, false).iv(obj.iv).crypt(obj.data);
// calculate HMAC
var hmac = Crypto.hmac("SHA256", passphrase).update(plaintext).final();
// check if decryption was successfull
if (hmac == obj.hmac) {
return JSON.parse(plaintext);
}
return null;
}
var passphrase = "D4s 1s7 d4s H4us v0m N1c0l4us!!1";
var input = { text: "Hi, this is top secret. We should protect it.", x: 42 };
log("input: " + JSON.stringify(input));
var encrypted = encryptObject(input, passphrase);
log("encrypted: " + JSON.stringify(encrypted));
var decrypted = decryptObject(encrypted, passphrase);
log("decrypted: " + JSON.stringify(decrypted));
JS: input: {"text":"Hi, this is top secret. We should protect it.","x":42}
JS: encrypted: {"iv":"357bf81d63418a6fdedce56558667d29","data":"f0a454da39d7306770afc7ea781904b2d49b4adb9d4f1790898d28145503a8d2183e4c53ec4e696a95938ee1c25bf5d29b24851c45a31fc6e13842887b267c","hmac":"9502652073243900ae42a4b864b05d17c3ec18b0983e3aa9ae76099be5642bac"}
JS: decrypted: {"text":"Hi, this is top secret. We should protect it.","x":42}