@sphereon/ssi-sdk-ext.x509-utils
Version:
Sphereon SSI-SDK plugin functions for X.509 Certificate handling.
81 lines (68 loc) • 2.75 kB
text/typescript
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
}
}