function longToByteArray(/*long*/ long: number): Uint8Array {
const byteArray = new Uint8Array(8)
for (let index = 0; index < byteArray.length; index++) {
// tslint:disable-next-line: no-bitwise
const byte = long & 0xff
byteArray[index] = byte
// tslint:disable-next-line: no-bitwise
long = (long - byte) >> 8
}
return byteArray
}
function arrayBufferToBase64(buffer: ArrayBuffer): string {
let binary = ''
const bytes = new Uint8Array(buffer)
const len = bytes.byteLength
for (let i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i])
}
return window.btoa(binary)
}
function base64UrlEncoded(input: ArrayBuffer): string {
const base64Encoded = arrayBufferToBase64(input)
return base64Encoded
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/\=+$/, '')
}
async function getAESKey(): Promise<CryptoKey> {
const aesKey = await window.crypto.subtle.generateKey(
{
name: 'AES-CBC',
length: 256
},
true,
['encrypt', 'decrypt']
)
return aesKey
}
async function exportCryptoKey(key: CryptoKey): Promise<Uint8Array> {
const keyBuffer = await window.crypto.subtle.exportKey('raw', key)
return new Uint8Array(keyBuffer)
}
async function getHMACKey(): Promise<CryptoKey> {
const rawSignKey = window.crypto.getRandomValues(new Uint8Array(32))
const hmacKey = await window.crypto.subtle.importKey(
'raw',
rawSignKey,
{
name: 'HMAC',
hash: { name: 'SHA-512' }
},
true,
['sign']
)
return hmacKey
}
async function getRsaPublicKey(jwk: JsonWebKey): Promise<CryptoKey> {
const importedKey = await window.crypto.subtle.importKey(
'jwk',
jwk,
{
name: 'RSA-OAEP',
hash: { name: 'sha-256' }
},
false,
['encrypt']
)
return importedKey
}
async function getEncryptedKeys(rsaPublicKey: CryptoKey, hmacKey: CryptoKey, aesKey: CryptoKey): Promise<ArrayBuffer> {
const exportedAESKey = await exportCryptoKey(aesKey)
const exportedHMACKey = await exportCryptoKey(hmacKey)
const keysBuffer = new Uint8Array(64)
keysBuffer.set(exportedHMACKey, 0)
keysBuffer.set(exportedAESKey, 32)
const encryptedKeys = await window.crypto.subtle.encrypt(
{
name: 'RSA-OAEP'
},
rsaPublicKey,
keysBuffer
)
return encryptedKeys
}
async function getAuthenticationTag(
hmacKey: CryptoKey,
a: Uint8Array,
iv: Uint8Array,
e: ArrayBuffer
): Promise<Uint8Array> {
const al = longToByteArray(a.byteLength * 8).reverse()
const signaturePayload = new Uint8Array(a.byteLength + iv.byteLength + e.byteLength + al.length)
signaturePayload.set(a, 0)
signaturePayload.set(iv, a.byteLength)
signaturePayload.set(new Uint8Array(e), iv.byteLength + a.byteLength)
signaturePayload.set(al, 16 + a.byteLength + e.byteLength)
const signedBuffer = await window.crypto.subtle.sign('HMAC', hmacKey, signaturePayload)
return new Uint8Array(signedBuffer).slice(0, 32)
}
export const encryptMessage = async (data: string, jwk: JsonWebKey): Promise<string> => {
const enc = new TextEncoder()
const p = enc.encode(data)
const protocalHeader = {
alg: 'RSA-OAEP-256',
enc: 'A256CBC-HS512',
typ: 'JWT',
jwk: jwk
}
const aesKey = await getAESKey()
const hmacKey = await getHMACKey()
const rsaPublicKey = await getRsaPublicKey(jwk)
const iv = crypto.getRandomValues(new Uint8Array(16))
const e = await window.crypto.subtle.encrypt(
{
name: 'AES-CBC',
iv
},
aesKey,
p
)
const encryptedKeys = await getEncryptedKeys(rsaPublicKey, hmacKey, aesKey)
const encodedProtocolHeader = enc.encode(JSON.stringify(protocalHeader))
const a = enc.encode(base64UrlEncoded(encodedProtocolHeader.buffer as ArrayBuffer))
const authenticationTag = (await getAuthenticationTag(hmacKey, a, iv, e)).buffer
const jwe =
base64UrlEncoded(encodedProtocolHeader.buffer as ArrayBuffer) +
'.' +
base64UrlEncoded(encryptedKeys) +
'.' +
base64UrlEncoded(iv.buffer as ArrayBuffer) +
'.' +
base64UrlEncoded(e) +
'.' +
base64UrlEncoded(authenticationTag as ArrayBuffer)
return jwe
}
Created
June 30, 2020 04:56
-
-
Save QingpingMeng/2313a08f6d86a4c1ac7eae7e52b2e54b to your computer and use it in GitHub Desktop.
AEAD
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment