diff --git a/spec/node/services/nodeCryptoFunction.service.spec.ts b/spec/node/services/nodeCryptoFunction.service.spec.ts index e57527918f..3bd8835127 100644 --- a/spec/node/services/nodeCryptoFunction.service.spec.ts +++ b/spec/node/services/nodeCryptoFunction.service.spec.ts @@ -161,6 +161,15 @@ describe('NodeCrypto Function Service', () => { }); }); + describe('rsaExtractPublicKey', () => { + it('should successfully extract key', async () => { + const nodeCryptoFunctionService = new NodeCryptoFunctionService(); + const privKey = Utils.fromB64ToArray(RsaPrivateKey); + const publicKey = await nodeCryptoFunctionService.rsaExtractPublicKey(privKey.buffer); + expect(Utils.fromBufferToB64(publicKey)).toBe(RsaPublicKey); + }); + }); + 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 ca517eaec2..92c80e090d 100644 --- a/spec/web/services/webCryptoFunction.service.spec.ts +++ b/spec/web/services/webCryptoFunction.service.spec.ts @@ -247,6 +247,15 @@ describe('WebCrypto Function Service', () => { }); }); + describe('rsaExtractPublicKey', () => { + it('should successfully extract key', async () => { + const cryptoFunctionService = getWebCryptoFunctionService(); + const privKey = Utils.fromB64ToArray(RsaPrivateKey); + const publicKey = await cryptoFunctionService.rsaExtractPublicKey(privKey.buffer); + expect(Utils.fromBufferToB64(publicKey)).toBe(RsaPublicKey); + }); + }); + describe('randomBytes', () => { it('should make a value of the correct length', async () => { const cryptoFunctionService = getWebCryptoFunctionService(); diff --git a/src/abstractions/cryptoFunction.service.ts b/src/abstractions/cryptoFunction.service.ts index 553444b667..d45221ea2d 100644 --- a/src/abstractions/cryptoFunction.service.ts +++ b/src/abstractions/cryptoFunction.service.ts @@ -16,6 +16,7 @@ export abstract class CryptoFunctionService { aesDecryptFast: (parameters: DecryptParameters) => Promise; aesDecrypt: (data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer) => Promise; rsaEncrypt: (data: ArrayBuffer, publicKey: ArrayBuffer, algorithm: 'sha1' | 'sha256') => Promise; - rsaDecrypt: (data: ArrayBuffer, key: ArrayBuffer, algorithm: 'sha1' | 'sha256') => Promise; + rsaDecrypt: (data: ArrayBuffer, privateKey: ArrayBuffer, algorithm: 'sha1' | 'sha256') => Promise; + rsaExtractPublicKey: (privateKey: ArrayBuffer) => Promise; randomBytes: (length: number) => Promise; } diff --git a/src/services/nodeCryptoFunction.service.ts b/src/services/nodeCryptoFunction.service.ts index 08b19dd0dd..ee154c06e6 100644 --- a/src/services/nodeCryptoFunction.service.ts +++ b/src/services/nodeCryptoFunction.service.ts @@ -133,6 +133,16 @@ export class NodeCryptoFunctionService implements CryptoFunctionService { return Promise.resolve(this.toArrayBuffer(decipher)); } + async rsaExtractPublicKey(privateKey: ArrayBuffer): Promise { + const privateKeyByteString = Utils.fromBufferToByteString(privateKey); + const privateKeyAsn1 = forge.asn1.fromDer(privateKeyByteString); + const forgePrivateKey = (forge as any).pki.privateKeyFromAsn1(privateKeyAsn1); + const forgePublicKey = (forge.pki as any).setRsaPublicKey(forgePrivateKey.n, forgePrivateKey.e); + const publicKeyAsn1 = (forge.pki as any).publicKeyToAsn1(forgePublicKey); + const publicKeyByteString = forge.asn1.toDer(publicKeyAsn1).data; + return Utils.fromByteStringToArray(publicKeyByteString).buffer; + } + randomBytes(length: number): Promise { return new Promise((resolve, reject) => { crypto.randomBytes(length, (error, bytes) => { diff --git a/src/services/webCryptoFunction.service.ts b/src/services/webCryptoFunction.service.ts index f055df3206..f5617236b9 100644 --- a/src/services/webCryptoFunction.service.ts +++ b/src/services/webCryptoFunction.service.ts @@ -204,6 +204,25 @@ export class WebCryptoFunctionService implements CryptoFunctionService { return await this.subtle.decrypt(rsaParams, impKey, data); } + async rsaExtractPublicKey(privateKey: ArrayBuffer): Promise { + const rsaParams = { + name: 'RSA-OAEP', + // Have to specify some algorithm + hash: { name: this.toWebCryptoAlgorithm('sha1') }, + }; + const impPrivateKey = await this.subtle.importKey('pkcs8', privateKey, rsaParams, true, ['decrypt']); + const jwkPrivateKey = await this.subtle.exportKey('jwk', impPrivateKey); + const jwkPublicKeyParams = { + kty: 'RSA', + e: jwkPrivateKey.e, + n: jwkPrivateKey.n, + alg: 'RSA-OAEP', + ext: true, + }; + const impPublicKey = await this.subtle.importKey('jwk', jwkPublicKeyParams, rsaParams, true, ['encrypt']); + return await this.subtle.exportKey('spki', impPublicKey); + } + randomBytes(length: number): Promise { const arr = new Uint8Array(length); this.crypto.getRandomValues(arr);