Subscribe to my email list now at http://jauyeung.net/subscribe/
Follow me on Twitter at https://twitter.com/AuMayeung
Many more articles at https://medium.com/@hohanga
The window
object is a global object that has that provides JavaScript access to the DOM. It also contains a standard library of functions that can we access at any location in our web apps.
In this article, we look at the window.cryoto
object.
window.crypto
The window.crypto
property returns a Crypto
object which is associated with the global object. This object allows web pages to run various cryptographic operations on the browser side. It has one property, which is the subtle
property.
The Crypto.subtle
property returns a SubtleCrypto
object which allows us to do subtle cryptography on the client-side. The SubtleCrypto
object has 5 methods for scrambling and unscrambling data. The sign
method is for creating digital signatures.
A verify
method exists to verify the digital signatures created by the sign
method.
The encrypt
method is used for encrypting data, and the decrypt
method is used for decryption the scrambled data generated by the encrypt
method. The digest
method is used to create a fixed-length, collision-resistant digest of some data.
We can also use the SubtleCrypto
object to generate and derive cryptographic keys with the generateKey
and deriveKey
methods respectively.
The generateKey
method generates a new distinct key value each time we call it, while the deriveKey
method derives a key from some initial material. If we provide the same material to 2 separate calls to deriveKey
, we will get the same underlying value.
The deriveKey
method is useful for deriving the same key for encryption and decryption. We can also use the importKey
and exportKey
methods to import and export cryptographic keys respectively.
There’s also a wrapKey
method that exports the key and then encrypts it with another key.
An unwrapKey
method is also provided to decrypt the encrypted key done by the wrapKey
method and import the decrypted key.
For example, we can use the sign
method to create a digital signature. It takes 3 arguments. The first is the algorithm
, which is a string or an object that specifies the signature algorithm to use for creating the digital signature. Possible values are:
- RSASSA-PKCS1-v1_5 — pass in the string
“RSASSA-PKCS1-v1_5”
or an object of the form{ “name”: “RSASSA-PKCS1-v1_5” }
- RSA-PSS — pass an
RsaPssParams
object. AnRsaPssParams
object has thename
property which should beRSA-PSS
, andsaltLength
which is the length of the random salt to use measured in bytes. The maximum value ofsaltLength
isMath.ceil((keySizeInBits - 1)/8) - digestSizeInBytes - 2
- ECDSA — pass an
EcdsaParams
object. AnEcdsaParams
object has thename
property, which should be the string'ECDSA'
and thehash
property, which is string that can have the possible values ofSHA-256
,SHA-384
orSHA-512
- HMAC — pass in the string
“HMAC”
or an object of the form{ “name”: “HMAC” }
The second argument is the key
which is a CryptoKey object that has the private key to be used for creating the signature. The third argument is the data
which is an ArrayBuffer
or ArrayBufferView
object that has the data to be signed.
The sign
method returns a promise that’s fulfilled with an ArrayBuffer
object that has the signature.
Likewise, the verify
method takes in the same first algorithm
, key
, and data
argument as the sign
method as the first, second and fourth arguments. The signature
generated from the sign
method is the third argument. It returns a promise that fulfills with the value true
if the signature is valid and false
otherwise.
To use the sign
and verify
methods, we can write something like the following code:
const enc = new TextEncoder();
const encodedMessage = enc.encode('hello');
const keyPair = window.crypto.subtle.generateKey({
name: "RSASSA-PKCS1-v1_5",
modulusLength: 4096,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256"
},
true,
["sign", "verify"]
);
(async () => {
const {
privateKey,
publicKey
} = await keyPair;
const signature = await window.crypto.subtle.sign(
"RSASSA-PKCS1-v1_5",
privateKey,
encodedMessage
);
const signatureValid = await window.crypto.subtle.verify("RSASSA-PKCS1-v1_5", publicKey, signature, encodedMessage);
console.log(signatureValid);
})()
We first generate the key pair with the generateKey
since we’re using the asymmetric RSA algorithm which has a private and public key. The generateKey
method takes the algorithm as the first argument, where the possible values are:
- To use RSASSA-PKCS1-v1_5, RSA-PSS, or RSA-OAEP we pass an RsaHashedKeyGenParams object.
- To use ECDSA or ECDH we pass an EcKeyGenParams object.
- To use HMAC we pass an HmacKeyGenParams object.
- To use AES-CTR, AES-CBC, AES-GCM, or AES-KW we pass an AesKeyGenParams object.
The second argument is the boolean extractable
property which indicates whether it’s possible to export a key using the SubtleCrypto.exportKey()
or SubtleCrypto.wrapKey()
methods. The third argument is the keyUsages
array which indicates which methods can be used with the keys generated:
-
encrypt
-
decrypt
-
sign
-
verify
-
deriveKey
-
deriveBits
-
wrapKey
-
unwrapKey
Then we call the sign
method with the algorithm name, private key, and the encoded message we generated from the TextEncoder
. This generates the signature. Then we call the verify
method with the algorithm name, private key, the generated signature fulfilled from the sign
method and the same encoded message. If we run the code above, we should get console.log
logging true
.
The encrypt
method takes 3 arguments. The first is the algorithm
, which is an object with the following possible values:
- To use the RSA-OAEP algorithm, we pass in an
RsaOaepParams
object. Full details of the properties of the object are at https://developer.mozilla.org/en-US/docs/Web/API/RsaOaepParams - To use AES-CTR algorithm, we pass in an
AesCtrParams
object. Full details of the properties of the object are at https://developer.mozilla.org/en-US/docs/Web/API/AesCtrParams - To use AES-CBC algorithm, we pass in an
AesCbcParams
object. Full details of the properties of the object are at https://developer.mozilla.org/en-US/docs/Web/API/AesCbcParams - To use AES-GCM algorithm, we pass in an
AesGcmParams
object. Full details of the properties of the object are at https://developer.mozilla.org/en-US/docs/Web/API/AesGcmParams
The second argument is the CryptoKey
object which we use to do the encryption. The third argument is a BufferSource
object that has the data to be encrypted, also known as plain text. It returns a promise that’s resolved with an ArrayBuffer
object containing the encrypted plain text.
Likewise, the decrypt
method takes the same first 2 arguments as the encrypt
method, except that the third argument is a BufferSource
that has the data to be decrypted. It returns a promise that’s resolved with the ArrayBuffer
object which has the plain text.
For example, we can use the encrypt
and dercrypt
methods like in the following code:
const enc = new TextEncoder();
const dec = new TextDecoder();
const keyPair = window.crypto.subtle.generateKey({
name: "RSA-OAEP",
modulusLength: 4096,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256"
},
true,
["encrypt", "decrypt"]
);
const encodedMessage = enc.encode('hello');
(async () => {
const {
privateKey,
publicKey
} = await keyPair;
const encryptedText = await window.crypto.subtle.encrypt({
name: "RSA-OAEP"
},
publicKey,
encodedMessage
)
console.log(encryptedText);
const decryptedText = await window.crypto.subtle.decrypt({
name: "RSA-OAEP"
},
privateKey,
encryptedText
)
console.log(decryptedText);
console.log(dec.decode(decryptedText));
})()
In the code above, we first generate a key or key pair with the generateKey
method with depending if the encryption algorithm is symmetric or asymmetric like we did with the sign
and verify
example. Asymmetric cryptographic algorithms have a public and private key like in the example above. RSA is an asymmetric algorithm.
Then we encode the message with the TextEncoder
to encode it to a ArrayBuffer
object which can used with the encrypt
method.
Then we use the encrypt
method with the algorithm, the public key, and the ArrayBuffer
object with the encoded text passed int to encrypt the data. Then to decrypt the encrypted text, we use the decrypt
method with the algorithm object passed in as the first argument, then we pass in the private key from the key pair, then we pass in the encrypted text as the third argument to the decrypt
method.
This will get the decrypted data as an ArrayBuffer
, which we will decode with the TextDecoder
’s decode
method with the decrypted ArrayBuffer
to get back the original text. This means that the last console.log
statement to get us back 'hello'
.
The Crypto
object also has one method, which is the getRandomValues
method. The method will create a strong random value given a typed array. The method takes one argument. It takes a typed array, which is an Int8Array
, a Uint8Array
, an Int16Array
, a Uint16Array
, an Int32Array
, or a Uint32Array
. To improve performance, this method doesn’t generate numbers with a truly random number generator, but rather it uses a pseudo-random number generator to generate the number. The entries of the typed array passed into the argument will be overwritten by the random numbers generated by this method.
We can use the getRandomValues
method like in the following example:
let array = new Uint32Array(10);
window.crypto.getRandomValues(array);
for (const num of array) {
console.log(num);
}
In the code above, we generated a new Uint32Array
, which we pass into the getRandomValues
method. Then in the for...of
loop, we get the generated values which overwrote whatever entries were in the original array. We should see 10 random numbers from the console.log
, and each time we run the code above, we should get different results.
With the window.cryoto
object, we can encrypt and decrypt data by using well-know cryptographic algorithms on the browser. It supports both symmetric and asymmetric encryption, which let us encrypt data with different algorithms. Also, we can use it to generate digital signatures and verify them. We can also use it to get random numbers with the getRandomValues
method.