@microsoft/windows-admin-center-sdk
Version:
Microsoft - Windows Admin Center Shell
336 lines (334 loc) • 12.4 kB
JavaScript
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