import { Injectable } from '@nestjs/common'; import { AbstractActionHandler, AwsProperties, Format } from '../abstract-action.handler'; import { Action } from '../action.enum'; import * as Joi from 'joi'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { KeySpec, KeyUsage, KmsKey } from './kms-key.entity'; import { breakdownArn } from '../util/breakdown-arn'; import { KmsService } from './kms.service'; import * as crypto from 'crypto'; type QueryParams = { GrantTokens: string[]; KeyId: string; } interface StandardOutput { KeyId: string; KeySpec: KeySpec; KeyUsage: KeyUsage; PublicKey: string; CustomerMasterKeySpec: KeySpec; } interface EncryptDecrypt extends StandardOutput { KeyUsage: 'ENCRYPT_DECRYPT'; EncryptionAlgorithms: ('SYMMETRIC_DEFAULT' | 'RSAES_OAEP_SHA_1' | 'RSAES_OAEP_SHA_256' | 'SM2PKE')[]; } interface SignVerify extends StandardOutput { KeyUsage: 'SIGN_VERIFY'; SigningAlgorithms: ('RSASSA_PSS_SHA_256' | 'RSASSA_PSS_SHA_384' | 'RSASSA_PSS_SHA_512' | 'RSASSA_PKCS1_V1_5_SHA_256' | 'RSASSA_PKCS1_V1_5_SHA_384' | 'RSASSA_PKCS1_V1_5_SHA_512' | 'ECDSA_SHA_256' | 'ECDSA_SHA_384' | 'ECDSA_SHA_512' | 'SM2DSA')[]; } type Output = EncryptDecrypt | SignVerify | StandardOutput; @Injectable() export class GetPublicKeyHandler extends AbstractActionHandler { constructor( @InjectRepository(KmsKey) private readonly keyRepo: Repository, private readonly kmsService: KmsService, ) { super(); } format = Format.Json; action = Action.KmsGetPublicKey; validator = Joi.object({ KeyId: Joi.string().required(), GrantTokens: Joi.array().items(Joi.string()), }); protected async handle({ KeyId }: QueryParams, awsProperties: AwsProperties): Promise { const searchable = KeyId.startsWith('arn') ? breakdownArn(KeyId) : { service: 'kms', region: awsProperties.region, accountId: awsProperties.accountId, identifier: KeyId, }; const [ type, pk ] = searchable.identifier.split('/'); const keyId: Promise = type === 'key' ? Promise.resolve(pk) : this.kmsService.findKeyIdFromAlias(pk, searchable); const keyRecord = await this.keyRepo.findOne({ where: { id: await keyId, region: searchable.region, accountId: searchable.accountId, }}); const pubKeyObject = crypto.createPublicKey({ key: keyRecord.key,//.split(String.raw`\n`).join('\n'), format: 'pem', }); if (keyRecord.usage === 'ENCRYPT_DECRYPT') { return { CustomerMasterKeySpec: keyRecord.keySpec, EncryptionAlgorithms: [ "SYMMETRIC_DEFAULT" ], KeyId: keyRecord.arn, KeySpec: keyRecord.keySpec, KeyUsage: keyRecord.usage, PublicKey: Buffer.from(pubKeyObject.export({ format: 'der', type: 'spki', })).toString('base64'), } } if (keyRecord.usage === 'SIGN_VERIFY') { const PublicKey = Buffer.from(pubKeyObject.export({ format: 'der', type: 'spki', })).toString('base64') console.log({PublicKey}) return { CustomerMasterKeySpec: keyRecord.keySpec, KeyId: keyRecord.arn, KeySpec: keyRecord.keySpec, KeyUsage: keyRecord.usage, PublicKey, SigningAlgorithms: [ 'RSASSA_PKCS1_V1_5_SHA_256' ] } } return { CustomerMasterKeySpec: keyRecord.keySpec, KeyId: keyRecord.arn, KeySpec: keyRecord.keySpec, KeyUsage: keyRecord.usage, PublicKey: Buffer.from(pubKeyObject.export({ format: 'pem', type: 'spki', })).toString('utf-8'), } } }