From d73012efc8f822059436844f4b808f00bb2bfcb0 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Fri, 20 Apr 2018 15:32:25 -0400 Subject: [PATCH] rsa encrypt and decrypt tests --- .../nodeCryptoFunction.service.spec.ts | 67 +++++++++++++++- .../webCryptoFunction.service.spec.ts | 78 ++++++++++++++++++- src/misc/utils.ts | 12 +++ src/services/nodeCryptoFunction.service.ts | 50 ++++++++---- src/services/webCryptoFunction.service.ts | 33 ++++---- 5 files changed, 206 insertions(+), 34 deletions(-) diff --git a/spec/node/services/nodeCryptoFunction.service.spec.ts b/spec/node/services/nodeCryptoFunction.service.spec.ts index bb70e8e89b4..746a0563b42 100644 --- a/spec/node/services/nodeCryptoFunction.service.spec.ts +++ b/spec/node/services/nodeCryptoFunction.service.spec.ts @@ -2,9 +2,33 @@ import { NodeCryptoFunctionService } from '../../../src/services/nodeCryptoFunct import { Utils } from '../../../src/misc/utils'; +const RsaPublicKey = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl0Vawl/toXzkEvB82FEtqHP' + + '4xlU2ab/v0crqIfXfIoWF/XXdHGIdrZeilnRXPPJT1B9dTsasttEZNnua/0Rek/cjNDHtzT52irfoZYS7X6HNIfOi54Q+egP' + + 'RQ1H7iNHVZz3K8Db9GCSKPeC8MbW6gVCzb15esCe1gGzg6wkMuWYDFYPoh/oBqcIqrGah7firqB1nDedzEjw32heP2DAffVN' + + '084iTDjiWrJNUxBJ2pDD5Z9dT3MzQ2s09ew1yMWK2z37rT3YerC7OgEDmo3WYo3xL3qYJznu3EO2nmrYjiRa40wKSjxsTlUc' + + 'xDF+F0uMW8oR9EMUHgepdepfAtLsSAQIDAQAB'; +const RsaPrivateKey = 'MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXRVrCX+2hfOQS8Hz' + + 'YUS2oc/jGVTZpv+/Ryuoh9d8ihYX9dd0cYh2tl6KWdFc88lPUH11Oxqy20Rk2e5r/RF6T9yM0Me3NPnaKt+hlhLtfoc0h86L' + + 'nhD56A9FDUfuI0dVnPcrwNv0YJIo94LwxtbqBULNvXl6wJ7WAbODrCQy5ZgMVg+iH+gGpwiqsZqHt+KuoHWcN53MSPDfaF4/' + + 'YMB99U3TziJMOOJask1TEEnakMPln11PczNDazT17DXIxYrbPfutPdh6sLs6AQOajdZijfEvepgnOe7cQ7aeatiOJFrjTApK' + + 'PGxOVRzEMX4XS4xbyhH0QxQeB6l16l8C0uxIBAgMBAAECggEASaWfeVDA3cVzOPFSpvJm20OTE+R6uGOU+7vh36TX/POq92q' + + 'Buwbd0h0oMD32FxsXywd2IxtBDUSiFM9699qufTVuM0Q3tZw6lHDTOVG08+tPdr8qSbMtw7PGFxN79fHLBxejjO4IrM9lapj' + + 'WpxEF+11x7r+wM+0xRZQ8sNFYG46aPfIaty4BGbL0I2DQ2y8I57iBCAy69eht59NLMm27fRWGJIWCuBIjlpfzET1j2HLXUIh' + + '5bTBNzqaN039WH49HczGE3mQKVEJZc/efk3HaVd0a1Sjzyn0QY+N1jtZN3jTRbuDWA1AknkX1LX/0tUhuS3/7C3ejHxjw4Dk' + + '1ZLo5/QKBgQDIWvqFn0+IKRSu6Ua2hDsufIHHUNLelbfLUMmFthxabcUn4zlvIscJO00Tq/ezopSRRvbGiqnxjv/mYxucvOU' + + 'BeZtlus0Q9RTACBtw9TGoNTmQbEunJ2FOSlqbQxkBBAjgGEppRPt30iGj/VjAhCATq2MYOa/X4dVR51BqQAFIEwKBgQDBSIf' + + 'TFKC/hDk6FKZlgwvupWYJyU9RkyfstPErZFmzoKhPkQ3YORo2oeAYmVUbS9I2iIYpYpYQJHX8jMuCbCz4ONxTCuSIXYQYUcU' + + 'q4PglCKp31xBAE6TN8SvhfME9/MvuDssnQinAHuF0GDAhF646T3LLS1not6Vszv7brwSoGwKBgQC88v/8cGfi80ssQZeMnVv' + + 'q1UTXIeQcQnoY5lGHJl3K8mbS3TnXE6c9j417Fdz+rj8KWzBzwWXQB5pSPflWcdZO886Xu/mVGmy9RWgLuVFhXwCwsVEPjNX' + + '5ramRb0/vY0yzenUCninBsIxFSbIfrPtLUYCc4hpxr+sr2Mg/y6jpvQKBgBezMRRs3xkcuXepuI2R+BCXL1/b02IJTUf1F+1' + + 'eLLGd7YV0H+J3fgNc7gGWK51hOrF9JBZHBGeOUPlaukmPwiPdtQZpu4QNE3l37VlIpKTF30E6mb+BqR+nht3rUjarnMXgAoE' + + 'Z18y6/KIjpSMpqC92Nnk/EBM9EYe6Cf4eA9ApAoGAeqEUg46UTlJySkBKURGpIs3v1kkf5I0X8DnOhwb+HPxNaiEdmO7ckm8' + + '+tPVgppLcG0+tMdLjigFQiDUQk2y3WjyxP5ZvXu7U96jaJRI8PFMoE06WeVYcdIzrID2HvqH+w0UQJFrLJ/0Mn4stFAEzXKZ' + + 'BokBGnjFnTnKcs7nv/O8='; + describe('NodeCrypto Function Service', () => { describe('aesEncrypt', () => { - it('should successfully aes encrypt data', async () => { + it('should successfully encrypt data', async () => { const nodeCryptoFunctionService = new NodeCryptoFunctionService(); const iv = makeStaticByteArray(16); const key = makeStaticByteArray(32); @@ -12,10 +36,21 @@ describe('NodeCrypto Function Service', () => { const encValue = await nodeCryptoFunctionService.aesEncrypt(data.buffer, iv.buffer, key.buffer); expect(Utils.fromBufferToB64(encValue)).toBe('ByUF8vhyX4ddU9gcooznwA=='); }); + + it('should successfully encrypt and then decrypt data', async () => { + const nodeCryptoFunctionService = new NodeCryptoFunctionService(); + const iv = makeStaticByteArray(16); + const key = makeStaticByteArray(32); + const value = 'EncryptMe!'; + const data = Utils.fromUtf8ToArray(value); + const encValue = await nodeCryptoFunctionService.aesEncrypt(data.buffer, iv.buffer, key.buffer); + const decValue = await nodeCryptoFunctionService.aesDecryptSmall(encValue, iv.buffer, key.buffer); + expect(Utils.fromBufferToUtf8(decValue)).toBe(value); + }); }); describe('aesDecryptSmall', () => { - it('should successfully aes decrypt data', async () => { + it('should successfully decrypt data', async () => { const nodeCryptoFunctionService = new NodeCryptoFunctionService(); const iv = makeStaticByteArray(16); const key = makeStaticByteArray(32); @@ -26,7 +61,7 @@ describe('NodeCrypto Function Service', () => { }); describe('aesDecryptLarge', () => { - it('should successfully aes decrypt data', async () => { + it('should successfully decrypt data', async () => { const nodeCryptoFunctionService = new NodeCryptoFunctionService(); const iv = makeStaticByteArray(16); const key = makeStaticByteArray(32); @@ -36,6 +71,32 @@ describe('NodeCrypto Function Service', () => { }); }); + describe('rsaEncrypt', () => { + it('should successfully encrypt and then decrypt data', async () => { + const nodeCryptoFunctionService = new NodeCryptoFunctionService(); + const pubKey = Utils.fromB64ToArray(RsaPublicKey); + const privKey = Utils.fromB64ToArray(RsaPrivateKey); + const value = 'EncryptMe!'; + const data = Utils.fromUtf8ToArray(value); + const encValue = await nodeCryptoFunctionService.rsaEncrypt(data.buffer, pubKey.buffer, 'sha1'); + const decValue = await nodeCryptoFunctionService.rsaDecrypt(encValue, privKey.buffer, 'sha1'); + expect(Utils.fromBufferToUtf8(decValue)).toBe(value); + }); + }); + + describe('rsaDecrypt', () => { + it('should successfully decrypt data', async () => { + const nodeCryptoFunctionService = new NodeCryptoFunctionService(); + const privKey = Utils.fromB64ToArray(RsaPrivateKey); + const data = Utils.fromB64ToArray('A1/p8BQzN9UrbdYxUY2Va5+kPLyfZXF9JsZrjeEXcaclsnHurdxVAJcnbEqYMP3UXV' + + '4YAS/mpf+Rxe6/X0WS1boQdA0MAHSgx95hIlAraZYpiMLLiJRKeo2u8YivCdTM9V5vuAEJwf9Tof/qFsFci3sApdbATkorCT' + + 'zFOIEPF2S1zgperEP23M01mr4dWVdYN18B32YF67xdJHMbFhp5dkQwv9CmscoWq7OE5HIfOb+JAh7BEZb+CmKhM3yWJvoR/D' + + '/5jcercUtK2o+XrzNrL4UQ7yLZcFz6Bfwb/j6ICYvqd/YJwXNE6dwlL57OfwJyCdw2rRYf0/qI00t9u8Iitw=='); + const decValue = await nodeCryptoFunctionService.rsaDecrypt(data.buffer, privKey.buffer, 'sha1'); + expect(Utils.fromBufferToUtf8(decValue)).toBe('EncryptMe!'); + }); + }); + describe('randomBytes', () => { it('should make a value of the correct length', async () => { const nodeCryptoFunctionService = new NodeCryptoFunctionService(); diff --git a/spec/web/services/webCryptoFunction.service.spec.ts b/spec/web/services/webCryptoFunction.service.spec.ts index 8c6b5776fe8..f41698ed586 100644 --- a/spec/web/services/webCryptoFunction.service.spec.ts +++ b/spec/web/services/webCryptoFunction.service.spec.ts @@ -6,6 +6,30 @@ import { WebCryptoFunctionService } from '../../../src/services/webCryptoFunctio import { Utils } from '../../../src/misc/utils'; +const RsaPublicKey = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl0Vawl/toXzkEvB82FEtqHP' + + '4xlU2ab/v0crqIfXfIoWF/XXdHGIdrZeilnRXPPJT1B9dTsasttEZNnua/0Rek/cjNDHtzT52irfoZYS7X6HNIfOi54Q+egP' + + 'RQ1H7iNHVZz3K8Db9GCSKPeC8MbW6gVCzb15esCe1gGzg6wkMuWYDFYPoh/oBqcIqrGah7firqB1nDedzEjw32heP2DAffVN' + + '084iTDjiWrJNUxBJ2pDD5Z9dT3MzQ2s09ew1yMWK2z37rT3YerC7OgEDmo3WYo3xL3qYJznu3EO2nmrYjiRa40wKSjxsTlUc' + + 'xDF+F0uMW8oR9EMUHgepdepfAtLsSAQIDAQAB'; +const RsaPrivateKey = 'MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXRVrCX+2hfOQS8Hz' + + 'YUS2oc/jGVTZpv+/Ryuoh9d8ihYX9dd0cYh2tl6KWdFc88lPUH11Oxqy20Rk2e5r/RF6T9yM0Me3NPnaKt+hlhLtfoc0h86L' + + 'nhD56A9FDUfuI0dVnPcrwNv0YJIo94LwxtbqBULNvXl6wJ7WAbODrCQy5ZgMVg+iH+gGpwiqsZqHt+KuoHWcN53MSPDfaF4/' + + 'YMB99U3TziJMOOJask1TEEnakMPln11PczNDazT17DXIxYrbPfutPdh6sLs6AQOajdZijfEvepgnOe7cQ7aeatiOJFrjTApK' + + 'PGxOVRzEMX4XS4xbyhH0QxQeB6l16l8C0uxIBAgMBAAECggEASaWfeVDA3cVzOPFSpvJm20OTE+R6uGOU+7vh36TX/POq92q' + + 'Buwbd0h0oMD32FxsXywd2IxtBDUSiFM9699qufTVuM0Q3tZw6lHDTOVG08+tPdr8qSbMtw7PGFxN79fHLBxejjO4IrM9lapj' + + 'WpxEF+11x7r+wM+0xRZQ8sNFYG46aPfIaty4BGbL0I2DQ2y8I57iBCAy69eht59NLMm27fRWGJIWCuBIjlpfzET1j2HLXUIh' + + '5bTBNzqaN039WH49HczGE3mQKVEJZc/efk3HaVd0a1Sjzyn0QY+N1jtZN3jTRbuDWA1AknkX1LX/0tUhuS3/7C3ejHxjw4Dk' + + '1ZLo5/QKBgQDIWvqFn0+IKRSu6Ua2hDsufIHHUNLelbfLUMmFthxabcUn4zlvIscJO00Tq/ezopSRRvbGiqnxjv/mYxucvOU' + + 'BeZtlus0Q9RTACBtw9TGoNTmQbEunJ2FOSlqbQxkBBAjgGEppRPt30iGj/VjAhCATq2MYOa/X4dVR51BqQAFIEwKBgQDBSIf' + + 'TFKC/hDk6FKZlgwvupWYJyU9RkyfstPErZFmzoKhPkQ3YORo2oeAYmVUbS9I2iIYpYpYQJHX8jMuCbCz4ONxTCuSIXYQYUcU' + + 'q4PglCKp31xBAE6TN8SvhfME9/MvuDssnQinAHuF0GDAhF646T3LLS1not6Vszv7brwSoGwKBgQC88v/8cGfi80ssQZeMnVv' + + 'q1UTXIeQcQnoY5lGHJl3K8mbS3TnXE6c9j417Fdz+rj8KWzBzwWXQB5pSPflWcdZO886Xu/mVGmy9RWgLuVFhXwCwsVEPjNX' + + '5ramRb0/vY0yzenUCninBsIxFSbIfrPtLUYCc4hpxr+sr2Mg/y6jpvQKBgBezMRRs3xkcuXepuI2R+BCXL1/b02IJTUf1F+1' + + 'eLLGd7YV0H+J3fgNc7gGWK51hOrF9JBZHBGeOUPlaukmPwiPdtQZpu4QNE3l37VlIpKTF30E6mb+BqR+nht3rUjarnMXgAoE' + + 'Z18y6/KIjpSMpqC92Nnk/EBM9EYe6Cf4eA9ApAoGAeqEUg46UTlJySkBKURGpIs3v1kkf5I0X8DnOhwb+HPxNaiEdmO7ckm8' + + '+tPVgppLcG0+tMdLjigFQiDUQk2y3WjyxP5ZvXu7U96jaJRI8PFMoE06WeVYcdIzrID2HvqH+w0UQJFrLJ/0Mn4stFAEzXKZ' + + 'BokBGnjFnTnKcs7nv/O8='; + describe('WebCrypto Function Service', () => { describe('pbkdf2', () => { const regular256Key = 'pj9prw/OHPleXI6bRdmlaD+saJS4awrMiQsQiDjeu2I='; @@ -67,7 +91,7 @@ describe('WebCrypto Function Service', () => { }); describe('aesEncrypt', () => { - it('should successfully aes encrypt data', async () => { + it('should successfully encrypt data', async () => { const webCryptoFunctionService = getWebCryptoFunctionService(); const iv = makeStaticByteArray(16); const key = makeStaticByteArray(32); @@ -75,10 +99,32 @@ describe('WebCrypto Function Service', () => { const encValue = await webCryptoFunctionService.aesEncrypt(data.buffer, iv.buffer, key.buffer); expect(Utils.fromBufferToB64(encValue)).toBe('ByUF8vhyX4ddU9gcooznwA=='); }); + + it('should successfully encrypt and then decrypt small data', async () => { + const webCryptoFunctionService = getWebCryptoFunctionService(); + const iv = makeStaticByteArray(16); + const key = makeStaticByteArray(32); + const value = 'EncryptMe!'; + const data = Utils.fromUtf8ToArray(value); + const encValue = await webCryptoFunctionService.aesEncrypt(data.buffer, iv.buffer, key.buffer); + const decValue = await webCryptoFunctionService.aesDecryptSmall(encValue, iv.buffer, key.buffer); + expect(Utils.fromBufferToUtf8(decValue)).toBe(value); + }); + + it('should successfully encrypt and then decrypt large data', async () => { + const webCryptoFunctionService = getWebCryptoFunctionService(); + const iv = makeStaticByteArray(16); + const key = makeStaticByteArray(32); + const value = 'EncryptMe!'; + const data = Utils.fromUtf8ToArray(value); + const encValue = await webCryptoFunctionService.aesEncrypt(data.buffer, iv.buffer, key.buffer); + const decValue = await webCryptoFunctionService.aesDecryptLarge(encValue, iv.buffer, key.buffer); + expect(Utils.fromBufferToUtf8(decValue)).toBe(value); + }); }); describe('aesDecryptSmall', () => { - it('should successfully aes decrypt data', async () => { + it('should successfully decrypt data', async () => { const webCryptoFunctionService = getWebCryptoFunctionService(); const iv = makeStaticByteArray(16); const key = makeStaticByteArray(32); @@ -89,7 +135,7 @@ describe('WebCrypto Function Service', () => { }); describe('aesDecryptLarge', () => { - it('should successfully aes decrypt data', async () => { + it('should successfully decrypt data', async () => { const webCryptoFunctionService = getWebCryptoFunctionService(); const iv = makeStaticByteArray(16); const key = makeStaticByteArray(32); @@ -99,6 +145,32 @@ describe('WebCrypto Function Service', () => { }); }); + describe('rsaEncrypt', () => { + it('should successfully encrypt and then decrypt data', async () => { + const webCryptoFunctionService = getWebCryptoFunctionService(); + const pubKey = Utils.fromB64ToArray(RsaPublicKey); + const privKey = Utils.fromB64ToArray(RsaPrivateKey); + const value = 'EncryptMe!'; + const data = Utils.fromUtf8ToArray(value); + const encValue = await webCryptoFunctionService.rsaEncrypt(data.buffer, pubKey.buffer, 'sha1'); + const decValue = await webCryptoFunctionService.rsaDecrypt(encValue, privKey.buffer, 'sha1'); + expect(Utils.fromBufferToUtf8(decValue)).toBe(value); + }); + }); + + describe('rsaDecrypt', () => { + it('should successfully decrypt data', async () => { + const webCryptoFunctionService = getWebCryptoFunctionService(); + const privKey = Utils.fromB64ToArray(RsaPrivateKey); + const data = Utils.fromB64ToArray('A1/p8BQzN9UrbdYxUY2Va5+kPLyfZXF9JsZrjeEXcaclsnHurdxVAJcnbEqYMP3UXV' + + '4YAS/mpf+Rxe6/X0WS1boQdA0MAHSgx95hIlAraZYpiMLLiJRKeo2u8YivCdTM9V5vuAEJwf9Tof/qFsFci3sApdbATkorCT' + + 'zFOIEPF2S1zgperEP23M01mr4dWVdYN18B32YF67xdJHMbFhp5dkQwv9CmscoWq7OE5HIfOb+JAh7BEZb+CmKhM3yWJvoR/D' + + '/5jcercUtK2o+XrzNrL4UQ7yLZcFz6Bfwb/j6ICYvqd/YJwXNE6dwlL57OfwJyCdw2rRYf0/qI00t9u8Iitw=='); + const decValue = await webCryptoFunctionService.rsaDecrypt(data.buffer, privKey.buffer, 'sha1'); + expect(Utils.fromBufferToUtf8(decValue)).toBe('EncryptMe!'); + }); + }); + describe('randomBytes', () => { it('should make a value of the correct length', async () => { const webCryptoFunctionService = getWebCryptoFunctionService(); diff --git a/src/misc/utils.ts b/src/misc/utils.ts index 2b2c323ab2f..e02bd66f3f0 100644 --- a/src/misc/utils.ts +++ b/src/misc/utils.ts @@ -39,6 +39,14 @@ export class Utils { } } + static fromByteStringToArray(str: string): Uint8Array { + const arr = new Uint8Array(str.length); + for (let i = 0; i < str.length; i++) { + arr[i] = str.charCodeAt(i); + } + return arr; + } + static fromBufferToB64(buffer: ArrayBuffer): string { if (Utils.isNode) { return new Buffer(buffer).toString('base64'); @@ -62,6 +70,10 @@ export class Utils { } } + static fromBufferToByteString(buffer: ArrayBuffer): string { + return String.fromCharCode.apply(null, new Uint8Array(buffer)); + } + // ref: https://stackoverflow.com/a/40031979/1090359 static fromBufferToHex(buffer: ArrayBuffer): string { if (Utils.isNode) { diff --git a/src/services/nodeCryptoFunction.service.ts b/src/services/nodeCryptoFunction.service.ts index 8a495b4d603..3ab9db7a165 100644 --- a/src/services/nodeCryptoFunction.service.ts +++ b/src/services/nodeCryptoFunction.service.ts @@ -1,8 +1,11 @@ import * as constants from 'constants'; import * as crypto from 'crypto'; +import * as forge from 'node-forge'; import { CryptoFunctionService } from '../abstractions/cryptoFunction.service'; +import { Utils } from '../misc/utils'; + export class NodeCryptoFunctionService implements CryptoFunctionService { pbkdf2(password: string | ArrayBuffer, salt: string | ArrayBuffer, algorithm: 'sha256' | 'sha512', iterations: number): Promise { @@ -57,18 +60,32 @@ export class NodeCryptoFunctionService implements CryptoFunctionService { return Promise.resolve(this.toArrayBuffer(decBuf)); } - rsaDecrypt(data: ArrayBuffer, key: ArrayBuffer, algorithm: 'sha1' | 'sha256'): Promise { - if (algorithm !== 'sha1') { - throw new Error('only sha1 is supported on this platform.'); + rsaEncrypt(data: ArrayBuffer, publicKey: ArrayBuffer, algorithm: 'sha1' | 'sha256'): Promise { + let md: forge.md.MessageDigest; + if (algorithm === 'sha256') { + md = forge.md.sha256.create(); + } else { + md = forge.md.sha1.create(); } - 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(this.toArrayBuffer(decBuf)); + const dataBytes = Utils.fromBufferToByteString(data); + const key = this.toForgePublicKey(publicKey); + const decBytes: string = key.encrypt(dataBytes, 'RSA-OAEP', { md: md }); + return Promise.resolve(Utils.fromByteStringToArray(decBytes).buffer); + } + + rsaDecrypt(data: ArrayBuffer, privateKey: ArrayBuffer, algorithm: 'sha1' | 'sha256'): Promise { + let md: forge.md.MessageDigest; + if (algorithm === 'sha256') { + md = forge.md.sha256.create(); + } else { + md = forge.md.sha1.create(); + } + + const dataBytes = Utils.fromBufferToByteString(data); + const key = this.toForgePrivateKey(privateKey); + const decBytes: string = key.decrypt(dataBytes, 'RSA-OAEP', { md: md }); + return Promise.resolve(Utils.fromByteStringToArray(decBytes).buffer); } randomBytes(length: number): Promise { @@ -101,8 +118,15 @@ export class NodeCryptoFunctionService implements CryptoFunctionService { return new Uint8Array(buf).buffer; } - private toPem(key: ArrayBuffer): string { - const b64Key = ''; // TODO: key to b84 - return '-----BEGIN PRIVATE KEY-----\n' + b64Key + '\n-----END PRIVATE KEY-----'; + private toForgePrivateKey(key: ArrayBuffer): any { + const byteString = Utils.fromBufferToByteString(key); + const asn1 = forge.asn1.fromDer(byteString); + return (forge as any).pki.privateKeyFromAsn1(asn1); + } + + private toForgePublicKey(key: ArrayBuffer): any { + const byteString = Utils.fromBufferToByteString(key); + const asn1 = forge.asn1.fromDer(byteString); + return (forge as any).pki.publicKeyFromAsn1(asn1); } } diff --git a/src/services/webCryptoFunction.service.ts b/src/services/webCryptoFunction.service.ts index 2b32078688a..25efa06d0e8 100644 --- a/src/services/webCryptoFunction.service.ts +++ b/src/services/webCryptoFunction.service.ts @@ -23,7 +23,7 @@ export class WebCryptoFunctionService implements CryptoFunctionService { const passwordBytes = this.toByteString(password); const saltBytes = this.toByteString(salt); const derivedKeyBytes = (forge as any).pbkdf2(passwordBytes, saltBytes, iterations, forgeLen, algorithm); - return this.fromByteStringToBuf(derivedKeyBytes); + return Utils.fromByteStringToArray(derivedKeyBytes).buffer; } const wcLen = algorithm === 'sha256' ? 256 : 512; @@ -54,7 +54,7 @@ export class WebCryptoFunctionService implements CryptoFunctionService { const valueBytes = this.toByteString(value); md.update(valueBytes, 'raw'); - return this.fromByteStringToBuf(md.digest().data); + return Utils.fromByteStringToArray(md.digest().data).buffer; } const valueBuf = this.toBuf(value); @@ -68,7 +68,7 @@ export class WebCryptoFunctionService implements CryptoFunctionService { const hmac = (forge as any).hmac.create(); hmac.start(algorithm, keyBytes); hmac.update(valueBytes); - return this.fromByteStringToBuf(hmac.digest().getBytes()); + return Utils.fromByteStringToArray(hmac.digest().getBytes()).buffer; } const signingAlgorithm = { @@ -94,7 +94,7 @@ export class WebCryptoFunctionService implements CryptoFunctionService { decipher.start({ iv: ivBytes }); decipher.update(dataBuffer); decipher.finish(); - return this.fromByteStringToBuf(decipher.output.getBytes()); + return Utils.fromByteStringToArray(decipher.output.getBytes()).buffer; } async aesDecryptLarge(data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer): Promise { @@ -102,14 +102,25 @@ 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 { + async rsaEncrypt(data: ArrayBuffer, publicKey: 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']); + const impKey = await this.subtle.importKey('spki', publicKey, rsaParams, false, ['encrypt']); + return await this.subtle.encrypt(rsaParams, impKey, data); + } + + async rsaDecrypt(data: ArrayBuffer, privateKey: 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', privateKey, rsaParams, false, ['decrypt']); return await this.subtle.decrypt(rsaParams, impKey, data); } @@ -134,19 +145,11 @@ export class WebCryptoFunctionService implements CryptoFunctionService { if (typeof (value) === 'string') { bytes = forge.util.encodeUtf8(value); } else { - bytes = String.fromCharCode.apply(null, new Uint8Array(value)); + bytes = Utils.fromBufferToByteString(value); } return bytes; } - private fromByteStringToBuf(byteString: string): ArrayBuffer { - const arr = new Uint8Array(byteString.length); - for (let i = 0; i < byteString.length; i++) { - arr[i] = byteString.charCodeAt(i); - } - return arr.buffer; - } - private toWebCryptoAlgorithm(algorithm: 'sha1' | 'sha256' | 'sha512'): string { return algorithm === 'sha1' ? 'SHA-1' : algorithm === 'sha256' ? 'SHA-256' : 'SHA-512'; }