From 97fe01e131c6af906188a29d266d626e0a189090 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Thu, 19 Apr 2018 00:00:53 -0400 Subject: [PATCH] rsaDecrypt implementation --- src/abstractions/cryptoFunction.service.ts | 1 + src/services/nodeCryptoFunction.service.ts | 20 ++++++++++++++++++++ src/services/webCryptoFunction.service.ts | 15 +++++++++++++-- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/abstractions/cryptoFunction.service.ts b/src/abstractions/cryptoFunction.service.ts index cb755573ba..34ea7d7978 100644 --- a/src/abstractions/cryptoFunction.service.ts +++ b/src/abstractions/cryptoFunction.service.ts @@ -3,4 +3,5 @@ export abstract class CryptoFunctionService { iterations: number) => Promise; hash: (value: string | ArrayBuffer, algorithm: 'sha1' | 'sha256' | 'sha512') => Promise; hmac: (value: ArrayBuffer, key: ArrayBuffer, algorithm: 'sha1' | 'sha256' | 'sha512') => Promise; + rsaDecrypt: (data: ArrayBuffer, key: ArrayBuffer, algorithm: 'sha1' | 'sha256') => Promise; } diff --git a/src/services/nodeCryptoFunction.service.ts b/src/services/nodeCryptoFunction.service.ts index 9847ff198d..533647ca8c 100644 --- a/src/services/nodeCryptoFunction.service.ts +++ b/src/services/nodeCryptoFunction.service.ts @@ -1,4 +1,5 @@ import * as crypto from 'crypto'; +import * as constants from 'constants'; import { CryptoFunctionService } from '../abstractions/cryptoFunction.service'; @@ -56,6 +57,20 @@ export class NodeCryptoFunctionService implements CryptoFunctionService { return Promise.resolve(decBuf.buffer); } + rsaDecrypt(data: ArrayBuffer, key: ArrayBuffer, algorithm: 'sha1' | 'sha256'): Promise { + if (algorithm !== 'sha1') { + throw new Error('only sha1 is supported on this platform.'); + } + + const nodeData = this.toNodeBuffer(data); + const rsaKey: crypto.RsaPrivateKey = { + key: this.toPem(key), + padding: constants.RSA_PKCS1_OAEP_PADDING, + }; + const decBuf = crypto.publicDecrypt(rsaKey, nodeData); + return Promise.resolve(decBuf.buffer); + } + randomBytes(length: number): Promise { return new Promise((resolve, reject) => { crypto.randomBytes(length, (error, bytes) => { @@ -81,4 +96,9 @@ export class NodeCryptoFunctionService implements CryptoFunctionService { private toNodeBuffer(value: ArrayBuffer): Buffer { return Buffer.from(new Uint8Array(value) as any); } + + private toPem(key: ArrayBuffer): string { + const b64Key = ''; // TODO: key to b84 + return '-----BEGIN PRIVATE KEY-----\n' + b64Key + '\n-----END PRIVATE KEY-----'; + } } diff --git a/src/services/webCryptoFunction.service.ts b/src/services/webCryptoFunction.service.ts index 02351016c5..f83e36efc6 100644 --- a/src/services/webCryptoFunction.service.ts +++ b/src/services/webCryptoFunction.service.ts @@ -30,7 +30,7 @@ export class WebCryptoFunctionService implements CryptoFunctionService { const passwordBuf = this.toBuf(password); const saltBuf = this.toBuf(salt); - const alg: Pbkdf2Params = { + const pbkdf2Params: Pbkdf2Params = { name: 'PBKDF2', salt: saltBuf, iterations: iterations, @@ -38,7 +38,7 @@ export class WebCryptoFunctionService implements CryptoFunctionService { }; const impKey = await this.subtle.importKey('raw', passwordBuf, { name: 'PBKDF2' }, false, ['deriveBits']); - return await window.crypto.subtle.deriveBits(alg, impKey, wcLen); + return await window.crypto.subtle.deriveBits(pbkdf2Params, impKey, wcLen); } async hash(value: string | ArrayBuffer, algorithm: 'sha1' | 'sha256' | 'sha512'): Promise { @@ -102,6 +102,17 @@ export class WebCryptoFunctionService implements CryptoFunctionService { return await this.subtle.decrypt({ name: 'AES-CBC', iv: iv }, impKey, data); } + async rsaDecrypt(data: ArrayBuffer, key: ArrayBuffer, algorithm: 'sha1' | 'sha256'): Promise { + // Note: Edge browser requires that we specify name and hash for both key import and decrypt. + // We cannot use the proper types here. + const rsaParams = { + name: 'RSA-OAEP', + hash: { name: this.toWebCryptoAlgorithm(algorithm) }, + }; + const impKey = await this.subtle.importKey('pkcs8', key, rsaParams, false, ['decrypt']); + return await this.subtle.decrypt(rsaParams, impKey, data); + } + randomBytes(length: number): Promise { const arr = new Uint8Array(length); this.crypto.getRandomValues(arr);