The crypto module is available to use in your EdgeWorkers code bundles to support the Javascript crypto API. The JavaScript crypto API is based on the Web Crypto API.

Add the crypto module to the EdgeWorkers code bundle

These steps demonstrate how to reference public keys locally within your EdgeWorkers code bundle and pass them into the crypto built-in module. To use the crypto module you need to:

  • Create a key pair.

  • Update your EdgeWorkers function to use the public key and the crypto built-in module.

Using this method, you no longer need to pack crypto libraries into your EdgeWorkers code bundles.

📘

Before you start, you should find out if your organization has any guidelines about the source of key materials. For this example, we'll assume your organization allows the use of WebCrypto APIs in Chrome.

Create a key pair

In this example we'll create an ECDSA key using SubtleCrypto.generateKey().

  1. In the Chrome JavaScript console, run this code to generate your keys.
await crypto.subtle.generateKey(
    {
        name: "ECDSA",
        namedCurve: "P-256",
    },
    true, // Indicates the key can be exported.
    ["sign", "verify"]
)
.then(async pair => {
        let publicJwk = await crypto.subtle.exportKey("jwk", pair.publicKey);
        console.log("Public: %s", JSON.stringify(publicJwk));

        let privateJwk = await crypto.subtle.exportKey("jwk", pair.privateKey);
        console.log("Private: %s", JSON.stringify(privateJwk));
})
  1. Here's an example of output that contains the public and private keys.
Public: {"crv":"P-256","ext":true,"key_ops":["verify"],"kty":"EC","x":"Ttiko0FT0Eg7pAtW9u_l07waClao36qLlV8GHNqFjKM","y":"QuUcij33jYXB0Rf7ZODNhduL4wKSzeKZQjlLqFFxAH8"}
Private: {"crv":"P-256","d":"4V892xv40B7UPmbAOMs-MrHC0whqdV4vB9e1dJRfkIY","ext":true,"key_ops":["sign"],"kty":"EC","x":"Ttiko0FT0Eg7pAtW9u_l07waCl

📘

Every call to the generateKey() method creates new keys.

Use the keys in an EdgeWorkers function

You can now add the public key and the crypto built-in module to your EdgeWorkers function. This simple EdgeWorkers function uses the onClientRequest event handler and the request headers to pass the data and signature.

  1. Update your EdgeWorkers function so that it performs these operations:

    • Loads the public key using the importKey() method to verify a signature.

    • Converts the two input headers, verify-data and verify-sig, from a header-friendly base64 format to a Unit8Array. The encoding built-in module contains a number of helpers that transform from common input formats such as, base16, base64, and base64url, to JavaScript’s typed arrays.

    • Runs the verify() function on the Unit8Arrays representing the input headers.

Here's an example of an EdgeWorkers function after you've updated it to perform theses functions and added the key and crypto module.

📘

Make sure that the ext field of the JWK is false so that it agrees with the extractable field of the importKey() method.

import { crypto } from 'crypto';
import { base64 } from ' encoding ';

export async function onClientRequest(request) {
    let data = request.getHeader('verify-data')[0];
    let sig = request.getHeader('verify-sig')[0];
    
    let cryptoKey = await crypto.subtle.importKey(
            "jwk",
            {"crv":"P-256","ext":false,"key_ops":["verify"],"kty":"EC","x":"Ttiko0FT0Eg7pAtW9u_l07waClao36qLlV8GHNqFjKM","y":"QuUcij33jYXB0Rf7ZODNhduL4wKSzeKZQjlLqFFxAH8"},
            { name: "ECDSA", namedCurve: "P-256" },
            false,
            ["verify"]
    );

    data = base64.decode(data);
    sig = base64.decode(sig);

    let isOk = await crypto.subtle.verify(
           {
                name: "ECDSA",
                hash: "SHA-256",
            },
            cryptoKey,
            sig,
            data
    );

    if (isOk) {
        request.respondWith(200, {}, 'Signature checks out');
    }
    else {
        request.respondWith(403, {}, 'Invalid signature');
    }
}
  1. In addition to updating your EdgeWorkers code you also need to sign some data. To do this, paste the following code snippet into your JavaScript Console.
let privateKey = await crypto.subtle.importKey("jwk",  {"crv":"P-256","d":"4V892xv40B7UPmbAOMs-MrHC0whqdV4vB9e1dJRfkIY","ext":true,"key_ops":["sign"],"kty":"EC","x":"Ttiko0FT0Eg7pAtW9u_l07waClao36qLlV8GHNqFjKM","y":"QuUcij33jYXB0Rf7ZODNhduL4wKSzeKZQjlLqFFxAH8"}, { name: "ECDSA", namedCurve: "P-256" }, false, ['sign']);
let data = new TextEncoder().encode("secret");
let signature = await crypto.subtle.sign(
    {
        name: "ECDSA", hash: {name: "SHA-256"},
    },
    privateKey,
    data
);
let base64Sig = btoa(String.fromCharCode(...new Uint8Array(signature)));
console.log("sig: %s", base64Sig);

This snippet gets the ASCII encoding of the string “secret”.

The ASCII encoded string is then converted to an Unit8Array and is passed to the sign() function, along with the private CryptoKey we generated earlier.

  1. Here's an example of the console output you should see.
sig: GN1Yo5z0oKVEhhAmCz7X5sy6s4Uvqf++bdNfaX4lcPQjg7M6HIxf++ulb3F/BsyZXsOxaTbqOoJWQL9o0gybwA==

The value after “sig” is the base64 encoded signature.

📘

Each call to the function will result in a different signature.

  1. Use this curl command to pass the signature and the base64 encoded representation of the string “secret” to the EdgeWorkers function.
$ curl -i '<path-to-your-edgeworker>' -H "verify-data: c2VjcmV0" -H "verify-sig: GN1Yo5z0oKVEhhAmCz7X5sy6s4Uvqf++bdNfaX4lcPQjg7M6HIxf++ulb3F/BsyZXsOxaTbqOoJWQL9o0gybwA=="

You should get a 200 response. This indicates that the EdgeWorkers function properly read and verified the data and signature from the headers. If you change a character in the verify-data header, you will get a 403 response.

HTTP/1.1 200 OK
Date: Wed, 21 Sep 2022 19:18:01 GMT
Connection: keep-alive

CryptoKey Object

The CryptoKey interface represents a cryptographic key obtained from the importKey() SubtleCrypto method. After you create a key you can use it to verify a signature, encrypt, or decrypt data.

CryptoKey Object Properties

type

The type of key. This is a read-only string value.

extractable

Indicates whether the key can be extracted using exportKey() or wrapKey(). This is a read-only boolean value.

algorithm

Returns an object that contains the supported algorithm for the specified key and any extra parameters. This is a read-only value.

usages

Indicates what can be done with the key. This is a read-only array of strings value.

SubtleCrypto Object

The subtleCrypto interface provides several cryptographic functions. SubtleCrypto features are obtained through the subtle property of the Crypto object you get from the Crypto property.

importKey()

Imports a key from a promise that fulfills with the imported key as a CryptoKey object.

The following example demonstrates the use of importKey(), encrypt(), and decrypt(). It imports an AES-CBC key, using it to encrypt and then decrypt sample text. It verifies that the decrypted value is the same as the encrypted value.

const SOURCE: &str = r#"
import { crypto } from 'crypto';

        export async function onClientRequest(request) {
            let rawKey = new Uint8Array ([93,210,19,203,234,199,254,16,118,129,214,61,229,117,91,33]);
            let key = await crypto.subtle.importKey("raw", rawKey, {"name": "AES-CBC"}, false, ["encrypt","decrypt"]);
    
            let iv = new Uint8Array([237, 234, 45, 119, 168, 16, 178, 26, 14, 182, 253, 39, 79, 181, 180, 219]);
            let data = new Uint8Array([44, 237, 221, 235, 17, 155, 115, 79, 8, 211, 94, 216, 92, 183, 9, 106, 15, 210, 0, 52, 92, 163, 2, 222, 130, 70, 80, 132, 80, 243, 28, 110, 25, 18, 20, 98, 63, 51, 5, 136, 72, 206, 212, 46, 255, 220, 131, 188, 133, 109]);
    
            let encryptedData = await crypto.subtle.encrypt({name: "AES-CBC", iv: iv}, key, data);
            let decryptedData = await crypto.subtle.decrypt({name: "AES-CBC", iv: iv}, key, encryptedData);
            
            if (new Uint8Array(data).toString() === new Uint8Array(decryptedData).toString()) {
                request.respondWith(200, {}, 'same!');
            }
            else {
                request.respondWith(501, {}, 'different!');
            }
        }
ParameterTypeDescription
formatStringDescribes the data format of the key to import. It can be one of the following:
- raw
- pkcs8
- spki
- jwk
keyDataArrayBuffer
TypedArray
DataView
or JSONWebKey object
Contains the key in the given format.
algorithmObject

The AES-CBC and AES-CTR algorithms can be either an object or a string.
Defines the type of key to import and provides extra algorithm specific parameters.

The supported algorithms with formats are:
- RSASSA-PKCS1-v1_5: jwk, pkcs8, spki
- RSA-PSS: jwk, pkcs8, spki,
- HMAC: jwk, raw
- AES-CBC: jwk, raw
- ECDSA: jwk, pkcs8, raw, spki
- AES-CTR: jwk
- RSA-OAEP: jwk
extractableBooleanMust be set to false.
Indicates whether it will be possible to export the key.
keyUsagesArray of stringsIndicates what can be done with the key:
"encrypt",
"decrypt",
and "verify"

RSASSA-PKCS1-v1_5: jwk example

let key = await crypto.subtle.importKey("jwk",
       {
           kty: "RSA",
           e: "AQAB",
           n: "vGO3eU16ag9zRkJ4AK8ZUZrjbtp5xWK0LyFMNT8933evJoHeczexMUzSiXaLrEFSyQZortk81zJH3y41MBO_UFDO_X0crAquNrkjZDrf9Scc5-MdxlWU2Jl7Gc4Z18AC9aNibWVmXhgvHYkEoFdLCFG-2Sq-qIyW4KFkjan05IE",
           alg: "RS256",
       },
       { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
       false,
       ["verify"]);

RSASSA-PKCS1-v1_5: pkcs8 example

let key = await crypto.subtle.importKey("pkcs8",
        pem2ab(pemEncodedKey),
        { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
        false,
        ["sign"]
)

RSASSA-PKCS1-v1_5: spki example

let key = crypto.subtle.importKey("spki",
    pem2ab(pemEncodedKey),
    { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
    false,
    ["verify"])

RSA-PSS: jwk example

let key = await crypto.subtle.importKey("jwk",
       {
           kty: "RSA",
           e: "AQAB",
           n: "vGO3eU16ag9zRkJ4AK8ZUZrjbtp5xWK0LyFMNT8933evJoHeczexMUzSiXaLrEFSyQZortk81zJH3y41MBO_UFDO_X0crAquNrkjZDrf9Scc5-MdxlWU2Jl7Gc4Z18AC9aNibWVmXhgvHYkEoFdLCFG-2Sq-qIyW4KFkjan05IE",
           alg: "PS256"
       },
       { name: "RSA-PSS", hash: "SHA-256" },
       false,
       ["verify"]);

RSA-PSS: pkcs8 example

let key = await crypto.subtle.importKey("pkcs8",
        pem2ab(pemEncodedKey),
        { name: "RSA-PSS", hash: "SHA-256" },
        false,
        ["sign"]
)

RSA-PSS: spki example

let key = await crypto.subtle.importKey(
       "spki",
       pem2ab(pemEncodedKey),
       { name: "RSA-PSS", hash: "SHA-256" },
       false,
       ["verify"])

HMAC: jwk example

let format = "jwk";
let keyData = {
    "alg": "HS512",
    "ext": false,
    "k": "fJSIx_0QkyZ4hju2MdwbI8nczcmYHkZnqB6HRi_cJlsO5c_FMeX7yTqh5sJ65jXf8_PUduCjsZpCcL_ApmvyyQ",  //<-- Verified in Chrome that its length is 512
    "key_ops": [
        "sign",
        "verify"
    ],
    "kty": "oct"
};
let algorithm = {
    "name": "HMAC",
    "hash": "SHA-512"
};
let extractable = false;
let keyUsages = ["sign", "verify"];

let key = await crypto.subtle.importKey(format, keyData, algorithm, extractable, keyUsages);

HMAC: raw example

let format = "raw";
let keyData = new Uint8Array([4, 232, 90, 60, 241, 190, 56, 218, 108, 4, 168, 250, 230, 174, 166, 14, 227, 212, 12, 107, 54, 156, 21, 217, 10, 16, 23, 230, 177, 51, 82, 128, 62, 1, 187, 244, 255, 43, 78, 157, 2, 148, 164, 158, 103, 168, 234, 6, 106, 136, 91, 12, 143, 227, 196, 36, 55, 20, 106, 45, 203, 209, 93, 65, 244]);
let algorithm = {
    "name": "HMAC",
    "hash": "SHA-512",
};
let extractable = false;
let keyUsages = ["verify"];

let key = await crypto.subtle.importKey(format, keyData.buffer, algorithm, extractable, keyUsages);

AES-CBC: jwk example

let format = "jwk";
let keyData = {
    "alg": "A128CBC",
    "ext": false,
    "k": "aI9fw-VdJd-Hvi8t61JQ4w",
    "kty": "oct"
};
let algorithm = "AES-CBC";

let extractable = false;
let keyUsages = ["encrypt"];

let key = await crypto.subtle.importKey(format, keyData, algorithm, extractable, keyUsages);

AES-CBC: raw example

let keyData = new Uint8Array([242, 215, 100, 141, 106, 23, 155, 163, 74, 14, 164, 65, 179, 119, 146, 204, 178, 46, 90, 197, 0, 165, 114, 202, 32, 253, 219, 60, 53, 186, 234, 14]);

let key = await crypto.subtle.importKey(
    "raw",
    keyData,
    { name: "AES-CBC" },
    false,
    ["encrypt"]
);

ECDSA: jwk example

let format = "jwk";
    let keyData = {
        "kty": "EC",
        "crv": "P-256",
        "x": "GH4tmgb8eaqdA-n7KXgy1SJ3BjOaIq_Hj9pPKAiyALE",
        "y": "DsZu5YirOG5uIQ5tWGCYM8n2q4yTIZjTYByFdVwyUig",
        "ext": false
    };
    let algorithm = {
        "name": "ECDSA",
        "namedCurve": "P-256"
    };
    let extractable = false;
    let keyUsages = ["verify"];
    
    let key = await crypto.subtle.importKey(format, keyData, algorithm, extractable, keyUsages);

ECDSA: pkcs8 example

let key = await crypto.subtle.importKey("pkcs8",
            pem2ab(pemEncodedKey),
            { name: "ECDSA", namedCurve: "P-256" },
            false,
            ["sign"]
    )

ECDSA: raw example

let keyData = new Uint8Array([4, 232, 90, 60, 241, 190, 56, 218, 108, 4, 168, 250, 230, 174, 166, 14, 227, 212, 12, 107, 54, 156, 21, 217, 10, 16, 23, 230, 177, 51, 82, 128, 62, 1, 187, 244, 255, 43, 78, 157, 2, 148, 164, 158, 103, 168, 234, 6, 106, 136, 91, 12, 143, 227, 196, 36, 55, 20, 106, 45, 203, 209, 93, 65, 244]);
    let key = await crypto.subtle
        .importKey(
          "raw",
          keyData.buffer,
          { name: "ECDSA", namedCurve: "P-256" },
          false,
          ["verify"]
        )
        .catch((err) => {
          throw 'importKey() failed. Error: ' + err.message;
        });

ECDSA: spki example

let derEncodedKey = [48, 50, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 6, 3, 30, 0, 4, 109, 78, 102, 114, 132, 45, 253, 21, 44, 94, 147, 33, 211, 179, 203, 194, 80, 82, 169, 113, 88, 220, 174, 211, 40, 134, 224, 147];
    let derEncodedKeyBuffer = new Uint8Array(derEncodedKey);

    let key = await crypto.subtle
        .importKey(
          "spki",
          derEncodedKeyBuffer,
          { name: "ECDSA", namedCurve: "P-256" },
          false,
          ["verify"]
        )
        .catch((err) => {
          throw 'importKey() failed. Error: ' + err.message;
        });

AES-CTR: jwk example

let format = "jwk";
    let keyData = {
        "alg": "A128CTR",
        "ext": false,
        "k": "aI9fw-VdJd-Hvi8t61JQ4w",
        "kty": "oct"
    };
    let algorithm = {
        "name": "AES-CTR"
    };
    let extractable = false;
    let keyUsages = ["encrypt"];

    let key = await crypto.subtle.importKey(format, keyData, algorithm, extractable, keyUsages);

RSA-OAEP: jwk example

let format = "jwk";
    let keyData = {
        "kty": "RSA",
        "e": "AQAB",
        "n": "pDepsbn1dYDuNqfSD13OaZEPwBGZbEF4qvGwm2GB2jNrNIor5orS3-FOgSfGQQ2EL7eYrhPFp_An82lQNolWLK57Iv4Ucrr8CMt-aDhUq9eQXZ1az8-BHNoy2OxkqHiJU2xdgHuXqDaANa4vuNfwJzLNGFaUcpPRxXl9ZmVGP2BjM9yfch2E4xG2rcOtbCSL1cO7KCCGjSyLecrPv4xhbgKwhc0lG5cvSWjYWDPVisLiyu3rA6We2cnGeJ3m2bwQ15QwkP3J7qeZJpRLkxO7hVUmsqHyWCYmsjEGI1EHgTfeHLP4tT0D6iyYQml-Lkb5HOMxCkt9QQCsjYQwg2A9JQ",
        "alg": "RSA-OAEP-256",
        "ext": false
    };
    let algorithm = {
        "name": "RSA-OAEP",
        "hash": "SHA-256"
    };
    let extractable = false;
    let keyUsages = ["encrypt"];
    
    let key = await crypto.subtle.importKey(format, keyData, algorithm, extractable, keyUsages);

encrypt()

Returns a promise that fulfills with an ArrayBuffer containing the encrypted data.

ParameterTypeDescription
algorithmObjectSpecifies the algorithm to use and any extra parameters if required.

The supported algorithms are:
- AES-CBC
- RSA-OAEP
- AES-CTR
keyCryptoKeyContains the key to use for encryption.
dataArrayBuffer
TypedArray
or DataView
Contains the data to be encrypted.

decrypt()

Returns a promise that fulfills with an ArrayBuffer containing the decrypted data.

ParameterTypeDescription
algorithmObjectSpecifies the algorithm to use and any extra parameters if required.

The supported algorithms are:
- AES-CBC
- RSA-OAEP
- AES-CTR
keyCryptoKeyContains the key to uses for decryption.
dataArrayBuffer
TypedArray
or DataView
Contains the data to be encrypted.

verify()

Verifies a digital signature that returns a promise that fulfills with a boolean value: true if the signature is valid, false otherwise.

ParameterTypeDescription
algorithmObject

The RSASSA-PKCS1-v1_5 and HMAC algorithms can be either an object or a string
Defines the algorithm to use, algorithm choices, and extra parameters.

The supported algorithms are:
- HMAC
- RSA-PSS
- RSASSA-PKCS1-v1_5
- ECDSA
keyCryptoKeyContains the key to use to verify the signature.
signatureArrayBufferContains the signature to verify.
dataArrayBufferContains the data that needs its signature verified.

digest()

Generates a digest of the given data and returns a promise that fulfills with an ArrayBuffer containing the digest.

ParameterTypeDescription
algorithmstring or objectIncludes the name property and the string names the hash functions use. Supported values are:
"SHA-1"
"SHA-256"
"SHA-384"
"SHA-512"
dataArrayBuffer
TypedArray
or DataView
Contains the data to be digested.

The following example computes the SHA-256 hash of a static array buffer. The expected output is Encoded: d87303c776e767aa617e9e5c8a3238a2f7c3e98ea395165786d97322bd731415.

import { crypto } from 'crypto';
 
export async function onClientRequest(request) {
            let buf = new Uint8Array([71,117,115,32,97,110,100,32,79,119,101,110,32,115,97,121,32,104,105,46]);
 
            // Perform the digest            
            let encoded = await crypto.subtle.digest('SHA-256', buf);


            // Format the output into hex
            let walkable = Array.from(new Uint8Array(encoded));
            let hex = walkable.map((b) => b.toString(16).padStart(2, '0')).join('');
            
            request.respondWith(200, {}, "Encoded: " + hex);
        }

Crypto Object

Represents basic cryptography features available in the current context.

getRandomValues()

Returns cryptographically strong random values. The random values are written into the passed array.

import { crypto } from 'crypto';
 
export async function onClientRequest(request) {
    let array = new Uint32Array(1);
    crypto.getRandomValues(array);
    request.addHeader("X-Random-Number", array[0].toString());
}
ParameterTypeDescription
typedArrayInteger-based TypedArrayThe array contents are replaced with the newly generated random numbers. The amount of data is controlled by the length of the array.

Crypto Object Properties

subtle

Returns a SubtleCrypto object that provides access to common cryptographic primitives. This is a read-only value.

pem2ab()

Converts a PEM-encoded key string into an ArrayBuffer. The function converts valid text between matching BEGIN and END delimiters, as described in RFC-7468. A TypeError error is thrown if matching headers cannot be found, or if illegal characters appear within the delimiters.

import { crypto, pem2ab} from 'crypto';
 
export async function onClientRequest(request) {
  let pemEncodedKey = `-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAELScIYCjf+IOluv9pppNv0xIGXTBp
KlSNHLY0ZX554kjI8DknO3x8J5z+H31OX7spkrI6xdqj9Q0Ouoy6UmjJ3w==
-----END PUBLIC KEY-----`;
 
       let cryptoKey = await crypto.subtle.importKey(
            "spki",
            pem2ab(pemEncodedKey),
            { name: "RSA-PSS", hash: "SHA-256" },
            false,
            ["verify"])
            .catch((err) => request.respondWith(222, {}, "error text: " + err.message));
 
       let signature = new Uint8Array([77, 125, 140, 9, 158, 214, 29, 176, 0, 44, 34, 9, 111, 158, 2, 97, 66, 238, 89, 240, 146, 171, 200, 99, 133, 231, 60, 89, 44, 156, 26, 7, 111, 198, 231, 11, 224, 154, 151, 224, 84, 120, 183, 185, 175, 34, 165, 99, 240, 102, 21, 251, 211, 191, 61, 224, 181, 30, 10, 108, 93, 192, 218, 43]);
       let data = new Uint8Array([
  104, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33
]);
 
       globalThis.result = await crypto.subtle.verify(
           {
             name: "RSA-PSS"
           },
            cryptoKey,
            signature,
            data)
            .catch((err) => request.respondWith(222, {}, "error text: " + err.message));
}
ParameterTypeDescription
pemEncodedKeyPEM-encoded key stringAn ArrayBuffer to use as keyData in the SubtleCrypto.importKey.