UNPKG

@microsoft/windows-admin-center-sdk

Version:

Microsoft - Windows Admin Center Shell

336 lines (334 loc) 12.4 kB
import { Observable, of, throwError } from 'rxjs'; import { Base64ArrayBuffer } from './base64-array-buffer'; /** * Web Crypto interface class. * @dynamic */ export class Crypto { static algRsaOaepSha1Key = { name: 'RSA-OAEP', hash: { name: 'SHA-1' } }; static algHmacSha256Key = { name: 'HMAC', hash: { name: 'SHA-256' } }; /** * hash with SHA-256 * (If it doesn't support web based crypto, encode as base64.) * @param data the original data to hash. * @return Observable<string> the hash generated. */ static getSha256(data) { if (data == null) { const message = MsftSme.getStrings().MsftSmeShell.Core.Error.ArgumentNullError.message; return throwError(() => new Error(message.format('Crypto/getSha256', 'data'))); } const root = window; const isIE = !!root.msCrypto; if (isIE && root.msCrypto.subtle) { return Crypto.hash256IE(root.msCrypto.subtle, data); } if (root.crypto) { const subtle = root.crypto.subtle || root.crypto.webkitSubtle; if (subtle) { return Crypto.hash256WebAPI(subtle, data); } } return of(window.btoa(data)); } /** * encrypt with RSA/SHA-1 * * @param jwk the JSON Web Key. Single string with JSON.stringify format. * @param data the original data to hash. * @return Observable<string> the hash generated. */ static encryptRsaSha1(jwk, data) { const RsaSha1 = 'Crypto/encryptRsaSha1'; let message = ''; if (jwk == null) { message = MsftSme.getStrings().MsftSmeShell.Core.Error.ArgumentNullError.message; return throwError(() => new Error(message.format(RsaSha1, 'jwk'))); } if (data == null) { message = MsftSme.getStrings().MsftSmeShell.Core.Error.ArgumentNullError.message; return throwError(() => new Error(message.format(RsaSha1, 'data'))); } const root = window; const isIE = !!root.msCrypto; if (isIE && root.msCrypto.subtle) { return Crypto.encryptRsaIE(root.msCrypto.subtle, jwk, data); } if (root.crypto) { let subtle = root.crypto.subtle; if (subtle) { const jwkObject = JSON.parse(jwk); return Crypto.encryptRsaWebAPI(subtle, jwkObject, data); } subtle = root.crypto.webkitSubtle; if (subtle) { const jwkArray = Crypto.toArrayBufferView(jwk); return Crypto.encryptRsaWebAPI(subtle, jwkArray, data); } } message = MsftSme.getStrings().MsftSmeShell.Core.Error.UnknownBrowser.message; return throwError(() => new Error(message.format(RsaSha1))); } /** * sign with HMAC/SHA-256 * * @param key the key (base64 encoded). * @param data the original data to hash. (unicode - not utf8) * @return Observable<string> the hash generated. */ static signHmac(key, data) { let message = ''; if (key == null) { message = MsftSme.getStrings().MsftSmeShell.Core.Error.ArgumentNullError.message; return throwError(() => new Error(message.format('Crypto/signHmac', 'key'))); } if (data == null) { message = MsftSme.getStrings().MsftSmeShell.Core.Error.ArgumentNullError.message; return throwError(() => new Error(message.format('Crypto/encryptRsaSha1', 'data'))); } const root = window; const isIE = !!root.msCrypto; if (isIE && root.msCrypto.subtle) { return Crypto.signHmacIE(root.msCrypto.subtle, key, data); } if (root.crypto) { const subtle = root.crypto.subtle || root.crypto.webkitSubtle; if (subtle) { return Crypto.signHmacWebAPI(subtle, key, data); } } message = MsftSme.getStrings().MsftSmeShell.Core.Error.UnknownBrowser.message; return throwError(() => new Error(message.format('Crypto/signHmac'))); } /** * Hash with SHA-256 for WebAPI. * * @param subtle the Web API subtle. * @param data the original data to hash. * @return Observable<string> the hash generated. */ static hash256WebAPI(subtle, data) { return new Observable((observer) => { const array = Crypto.toUint8Array(data); subtle.digest({ name: 'SHA-256' }, array) .then((hash) => { const hexText = Crypto.toHexString(hash); observer.next(hexText); observer.complete(); }, (error) => { observer.error(error); }); }); } /** * Hash with SHA-256 for IE/Older API. * * @param subtle the older API subtle. * @param data the original data to hash. * @return Observable<string> the hash generated. */ static hash256IE(subtle, data) { return new Observable((observer) => { const array = Crypto.toUint8Array(data); const cryptoOp = subtle.digest({ name: 'SHA-256' }, array); cryptoOp.oncomplete = (event) => { const hexText = Crypto.toHexString(event.target.result); observer.next(hexText); observer.complete(); }; cryptoOp.onerror = (error) => { observer.error(error); }; cryptoOp.finish(); }); } /** * Encrypt with RSA/SHA-1 for WebAPI. * * @param subtle the Web API subtle. * @param jwkObject the JSON Web key object or ArrayBufferView on webkit. * @param data the original data to encrypt. * @return Observable<string> the encrypted base64 string. */ static encryptRsaWebAPI(subtle, jwkObject, data) { return new Observable((observer) => { const array = Crypto.toUtf8ArrayBuffer(data); subtle.importKey('jwk', jwkObject, Crypto.algRsaOaepSha1Key, false, ['encrypt']) .then((publicKey) => { return subtle.encrypt(Crypto.algRsaOaepSha1Key, publicKey, array); }) .then((result) => { observer.next(Crypto.createBase64(result)); observer.complete(); }, (error) => { observer.error(error); }); }); } /** * Encrypt with RSA/SHA-1 for IE/Older API. * * @param subtle the older API subtle. * @param jwk the JSON Web key format (Stringfiy). * @param data the original data to hash. * @return Observable<string> the encrypted base64 string. */ static encryptRsaIE(subtle, jwk, data) { return new Observable((observer) => { const array = Crypto.toUtf8ArrayBuffer(data); const onerror = (error) => { observer.error(error); }; const jwkBytes = Crypto.toArrayBuffer(jwk); const importKeyOp = subtle.importKey('jwk', jwkBytes, Crypto.algRsaOaepSha1Key, false, ['encrypt']); importKeyOp.oncomplete = (importKeyOpOnCompleteEvent) => { const publicKey = importKeyOpOnCompleteEvent.target.result; const encryptOp = subtle.encrypt(Crypto.algRsaOaepSha1Key, publicKey, array); encryptOp.oncomplete = (encryptOpOnCompleteEvent) => { observer.next(Crypto.createBase64(encryptOpOnCompleteEvent.target.result)); observer.complete(); }; encryptOp.onerror = onerror; }; importKeyOp.onerror = onerror; }); } /** * Sign with HMAC for WebAPI. * * @param subtle the Web API subtle. * @param key the key. * @param data the original data to encrypt. * @return Observable<string> the encrypted base64 string. */ static signHmacWebAPI(subtle, key, data) { return new Observable((observer) => { const array = Crypto.toUtf8ArrayBuffer(data); const keyObject = Crypto.toArrayBufferView(window.atob(key)); subtle.importKey('raw', keyObject, Crypto.algHmacSha256Key, false, ['sign']) .then((publicKey) => { return subtle.sign(Crypto.algHmacSha256Key, publicKey, array); }) .then((result) => { observer.next(Crypto.createBase64(result)); observer.complete(); }, (error) => { observer.error(error); }); }); } /** * Sign with HMAC for IE/Older API. * * @param subtle the older API subtle. * @param jwk the key. * @param data the original data to hash. * @return Observable<string> the encrypted base64 string. */ static signHmacIE(subtle, key, data) { return new Observable((observer) => { const array = Crypto.toUtf8ArrayBuffer(data); const onerror = (error) => { observer.error(error); }; const keyBytes = Crypto.toArrayBuffer(window.atob(key)); const importKeyOp = subtle.importKey('raw', keyBytes, Crypto.algHmacSha256Key, false, ['sign']); importKeyOp.oncomplete = (importKeyOpEvent) => { const publicKey = importKeyOpEvent.target.result; const encryptOp = subtle.sign(Crypto.algHmacSha256Key, publicKey, array); encryptOp.oncomplete = (encryptOpEvent) => { observer.next(Crypto.createBase64(encryptOpEvent.target.result)); observer.complete(); }; encryptOp.onerror = onerror; }; importKeyOp.onerror = onerror; }); } /** * Create hex string from byte raw data. * * @param bytes the array buffer. * @return string the hex string. */ static toHexString(bytes) { const array = new Uint8Array(bytes); let hexText = ''; for (let i = 0; i < array.length; i++) { const hex = array[i].toString(16); hexText += (hex.length === 1 ? '0' : '') + hex; } return hexText; } /** * Create Uint8Array from a string. * * @param data The string data. * @return Uint8Array The bytes array. */ static toUint8Array(data) { const temp = []; for (let i = 0; i < data.length; i++) { const ch = data.charCodeAt(i); temp.push((ch & 0x0ff00) >> 8); temp.push(ch & 0x0ff); } return new Uint8Array(temp); } /** * Create ArrayBuffer from a string with unicode. * * @param data The string data. * @return ArrayBuffer The bytes array. */ static toUtf8ArrayBuffer(data) { const utf8 = Crypto.utf8Encode(data); return Crypto.toArrayBuffer(utf8); } /** * Encode utf8 string. * * @param data the unencoded string. */ static utf8Encode(data) { return unescape(encodeURIComponent(data)); } /** * Create ArrayBuffer from a string with unicode. * * @param data The string data. * @return ArrayBuffer The bytes array. */ static toArrayBuffer(data) { const buffer = new ArrayBuffer(data.length); const view = new Uint8Array(buffer); for (let i = 0; i < buffer.byteLength; i++) { view[i] = data.charCodeAt(i); } return buffer; } /** * Create ArrayBufferView from a string with unicode. * * @param data The string data. * @return ArrayBuffer The bytes array. */ static toArrayBufferView(data) { const buffer = new ArrayBuffer(data.length); const view = new Uint8Array(buffer); for (let i = 0; i < buffer.byteLength; i++) { view[i] = data.charCodeAt(i); } return view; } /** * Create base64 from byte data. * * @param data The byte data. * @return string The base64 encoded data. */ static createBase64(data) { return Base64ArrayBuffer.Encode(data); } } //# sourceMappingURL=crypto.js.map