UNPKG

@sphereon/ssi-sdk-ext.x509-utils

Version:

Sphereon SSI-SDK plugin functions for X.509 Certificate handling.

81 lines (68 loc) 2.75 kB
import * as u8a from 'uint8arrays' import { HashAlgorithm, KeyVisibility } from '../types' import { globalCrypto } from './crypto' import { cryptoSubtleImportRSAKey, RSAEncryptionSchemes, RSASignatureSchemes } from './rsa-key' import { PEMToJwk } from './x509-utils' export class RSASigner { private readonly hashAlgorithm: HashAlgorithm private readonly jwk: JsonWebKey private key: CryptoKey | undefined private readonly scheme: RSAEncryptionSchemes | RSASignatureSchemes /** * * @param key Either in PEM or JWK format (no raw hex keys here!) * @param opts The algorithm and signature/encryption schemes */ constructor( key: string | JsonWebKey, opts?: { hashAlgorithm?: HashAlgorithm; scheme?: RSAEncryptionSchemes | RSASignatureSchemes; visibility?: KeyVisibility } ) { if (typeof key === 'string') { this.jwk = PEMToJwk(key, opts?.visibility) } else { this.jwk = key } this.hashAlgorithm = opts?.hashAlgorithm ?? 'SHA-256' this.scheme = opts?.scheme ?? 'RSA-PSS' } private getImportParams(): AlgorithmIdentifier | RsaPssParams { if (this.scheme === 'RSA-PSS') { return { name: this.scheme, saltLength: 32 } } return { name: this.scheme /*, hash: this.hashAlgorithm*/ } } private async getKey(): Promise<CryptoKey> { if (!this.key) { this.key = await cryptoSubtleImportRSAKey(this.jwk, this.scheme, this.hashAlgorithm) } return this.key } private bufferToString(buf: ArrayBuffer) { const uint8Array = new Uint8Array(buf) return u8a.toString(uint8Array, 'base64url') // Needs to be base64url for JsonWebSignature2020. Don't change! } public async sign(data: Uint8Array): Promise<string> { const input = data const key = await this.getKey() const signature = this.bufferToString(await globalCrypto(false).subtle.sign(this.getImportParams(), key, input)) if (!signature) { throw Error('Could not sign input data') } // base64url signature return signature } public async verify(data: string | Uint8Array, signature: string): Promise<boolean> { const jws = signature.includes('.') ? signature.split('.')[2] : signature const input = typeof data == 'string' ? u8a.fromString(data, 'utf-8') : data let key = await this.getKey() if (!key.usages.includes('verify')) { const verifyJwk = { ...this.jwk } delete verifyJwk.d delete verifyJwk.use delete verifyJwk.key_ops key = await cryptoSubtleImportRSAKey(verifyJwk, this.scheme, this.hashAlgorithm) } const verificationResult = await globalCrypto(false).subtle.verify(this.getImportParams(), key, u8a.fromString(jws, 'base64url'), input) return verificationResult } }