lemon-core
Version:
Lemon Serverless Micro-Service Platform
235 lines • 10.6 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.KeyVaultService = exports.fromBase64 = exports.isBase64 = void 0;
/**
* file: `cores/aws-kms-service.ts`
* - kms service for AWS KMS.
*
*
* **TODO**
* - [x] in VPC lambda, will timeout due to NAT => so, need to support via engine. => use `VPC Endpoints` with kms service.
* - [x] error of `not authorized` => use one of below.
* 1. add this Role (lemon-todaq-api-prod-ap-northeast-2-lambdaRole) to `Key users`.
* 2. add `kms:Decrypt` `kms:Encrypt` in resource of `iamRoleStatements`.
*
*
* @author Ian Kim <ian@lemoncloud.io>
* @date 2023-09-30 initial version.
*
* @copyright (C) lemoncloud.io 2023 - All Rights Reserved.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const engine_1 = require("../../engine");
require("dotenv/config");
const NS = engine_1.$U.NS('AZKV', 'blue'); // NAMESPACE TO BE PRINTED.
// type MySigningAlgorithm = SigningAlgorithmSpec;
const ALIAS = `lemon-hello-api`; //NOTE - use env[KMS_KEY_ID] to overide.
const region = () => engine_1.$engine.environ('REGION', 'ap-northeast-2');
//! get aws client for KMS
// const instance = () => {
// const _region = region();
// const config = { region: _region };
// return new AWS.KMS(config);
// };
const instance = () => {
return KeyVaultService.instance();
};
/**
* check if base64 string.
*/
const isBase64 = (text) => /^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$/.test(text);
exports.isBase64 = isBase64;
/**
* normal base64 to url encoded.
*/
const fromBase64 = (base64) => base64.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
exports.fromBase64 = fromBase64;
/**
* class: `KeyVaultService`
* - shared Key Management Service to encrypt/decrypt message.
*/
class KeyVaultService {
constructor(keyId, options) {
/**
* get name of this
*/
this.name = () => `KMS`;
/**
* hello
*/
this.hello = () => `azure-keyvault-service:${this._keyId}`;
/**
* get key-id to encrypt.
*/
this.keyId = () => {
if (!this._keyId || typeof this._keyId !== 'string')
throw new Error(`.keyId<${typeof this._keyId}> (string) is required!`);
return this._keyId;
};
this.instance = () => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { KeyClient, CryptographyClient, EncryptResult, DecryptResult } = require('@azure/keyvault-keys');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { DefaultAzureCredential } = require('@azure/identity');
const keyVault = process.env.KEY_VAULT_RESOURCE;
const vaultUrl = `https://${keyVault}.vault.azure.net/`;
const credentials = new DefaultAzureCredential();
const keyClient = new KeyClient(vaultUrl, credentials);
return { keyClient, credentials, CryptographyClient, EncryptResult, DecryptResult };
};
/**
* get KMS instance in stock
*/
// public instance() {
// if (!this._instance) this._instance = instance();
// return this._instance;
// }
/**
* Encrypt message
*
* @param {*} message
*/
this.encrypt = (message) => __awaiter(this, void 0, void 0, function* () {
const keyId = this.keyId();
(0, engine_1._inf)(NS, `encrypt(${keyId}, ${message.substring(0, 10)}...)..`);
const { keyClient, credentials, CryptographyClient } = this.instance();
const keyVaultKey = yield keyClient.getKey(keyId);
const cryptographyClient = new CryptographyClient(keyVaultKey, credentials);
try {
const EncryptResult = yield cryptographyClient.encrypt({
algorithm: 'RSA1_5',
plaintext: Buffer.from(message),
});
if (!Buffer.from(EncryptResult.result, 'hex').toString('base64')) {
throw new Error(`buffered ${EncryptResult} (string) is required!`);
}
return Buffer.from(EncryptResult.result, 'hex').toString('base64');
}
catch (err) {
console.log(err);
}
});
/**
* Decrypt message
*
* @param {*} encryptedSecret
*/
this.decrypt = (encryptedSecret) => __awaiter(this, void 0, void 0, function* () {
(0, engine_1._inf)(NS, `decrypt(${encryptedSecret.substring(0, 12)}...)..`);
const bufferedEncryptedSecret = Buffer.from(encryptedSecret, 'base64');
if (!bufferedEncryptedSecret) {
throw new Error(`${bufferedEncryptedSecret} (string) is required!`);
}
const keyId = this.keyId();
const { keyClient, credentials, CryptographyClient } = this.instance();
const keyVaultKey = yield keyClient.getKey(keyId);
const cryptographyClient = new CryptographyClient(keyVaultKey, credentials);
try {
const decryptedSecret = yield cryptographyClient.decrypt({
algorithm: 'RSA1_5',
ciphertext: bufferedEncryptedSecret,
});
return decryptedSecret.result.toString();
}
catch (err) {
console.log(err);
}
});
/**
* make signature by message
*
* @param {*} message any string
* @param forJwtSignature (option) flag to get JWT signature format.
*/
this.sign = (message, forJwtSignature = true) => __awaiter(this, void 0, void 0, function* () {
if (!message || typeof message !== 'string')
throw new Error(`@message[${message}] is invalid - kms.sign()`);
const keyId = this.keyId();
const { keyClient, credentials, CryptographyClient } = this.instance();
const keyVaultKey = yield keyClient.getKey(keyId);
const cryptographyClient = new CryptographyClient(keyVaultKey.id, credentials);
(0, engine_1._inf)(NS, `sign(${keyId}, ${message.substring(0, 10)}...)..`);
const result = yield cryptographyClient.signData('RS256', Buffer.from(message));
const signature = result.result;
return signature;
});
/**
* verify signature in asymetric way
* - it tooks around `30ms`
*
* @param {*} message any string
* @param {*} signature signature of Buffer or string(in base64)
*/
this.verify = (message, signature) => __awaiter(this, void 0, void 0, function* () {
if (!message || typeof message !== 'string')
throw new Error(`@message[${message}] is invalid - kms.verify()`);
if (!signature)
throw new Error(`@signature (string|Buffer) is required - kms.verify()`);
const keyId = this.keyId();
const { keyClient, credentials, CryptographyClient } = this.instance();
const keyVaultKey = yield keyClient.getKey(keyId);
const cryptographyClient = new CryptographyClient(keyVaultKey.id, credentials);
(0, engine_1._inf)(NS, `verify(${keyId}, ${message.substring(0, 10)}...)..`);
const result = yield cryptographyClient.verifyData('RS256', Buffer.from(message), Buffer.from(signature));
return result.result;
});
/**
* retrieve public-key for asymetric verification.
* - used to verify signature with JWT library w/o making request to AWS KMS.
* - in general, cache this `public-key` to verify locally.
*
* @param encoding (optional) encoding type
*/
this.getPublicKey = (encoding = 'base64') => __awaiter(this, void 0, void 0, function* () {
const keyId = this.keyId();
(0, engine_1._inf)(NS, `getPublicKey(${keyId})..`);
const { keyClient } = this.instance();
const result = yield keyClient.getKey(keyId);
return result.toString();
});
keyId = keyId !== null && keyId !== void 0 ? keyId : `${engine_1.$engine.environ(KeyVaultService.ENV_KMS_KEY_ID, KeyVaultService.DEF_KMS_TARGET)}`;
this._keyId = keyId;
this._options = options;
}
/**
* it should be 'hello lemon'
*
* # Example
* ```sh
* # encrypt text
* $ aws kms encrypt --profile <profile> --key-id <kms-key-id> --plaintext "hello lemon" --query CiphertextBlob --output text
* ```
*/
sample() {
return __awaiter(this, void 0, void 0, function* () {
(0, engine_1._inf)(NS, 'sample()..');
//! check key-id.
const message = `hello lemon!`;
const KMS_KEY_ID = engine_1.$engine.environ(KeyVaultService.ENV_KMS_KEY_ID, `alias/${ALIAS}`);
const keyId = this.keyId();
(0, engine_1._log)(NS, '> key-id =', keyId);
const encrypted = yield this.encrypt(message);
(0, engine_1._log)(NS, '> encrypted =', encrypted);
const decrypted = yield this.decrypt(encrypted);
(0, engine_1._log)(NS, '> decrypted =', decrypted, '->', message == decrypted ? 'ok' : 'error');
return { KMS_KEY_ID, keyId, message, encrypted, decrypted };
});
}
}
exports.KeyVaultService = KeyVaultService;
/**
* environ name of KMS KEY
*/
KeyVaultService.ENV_KMS_KEY_ID = 'KMS_KEY_ID';
KeyVaultService.DEF_KMS_TARGET = (_a = process.env.KEY_VAULT_KEYS) !== null && _a !== void 0 ? _a : `key-lemon`;
//# sourceMappingURL=azure-keyvault-service.js.map
;