UNPKG

@credo-ts/core

Version:

<p align="center"> <br /> <img alt="Credo Logo" src="https://github.com/openwallet-foundation/credo-ts/blob/c7886cb8377ceb8ee4efe8d264211e561a75072d/images/credo-logo.png" height="250px" /> </p> <h1 align="center"><b>Credo - Core</b></h1

1 lines • 19.1 kB
{"version":3,"file":"X509Certificate.mjs","names":[],"sources":["../../../src/modules/x509/X509Certificate.ts"],"sourcesContent":["import { AsnParser } from '@peculiar/asn1-schema'\nimport {\n id_ce_authorityKeyIdentifier,\n id_ce_extKeyUsage,\n id_ce_issuerAltName,\n id_ce_keyUsage,\n id_ce_subjectAltName,\n id_ce_subjectKeyIdentifier,\n SubjectPublicKeyInfo,\n} from '@peculiar/asn1-x509'\nimport * as x509 from '@peculiar/x509'\nimport type { AgentContext } from '../../agent'\nimport { CredoWebCrypto, CredoWebCryptoKey } from '../../crypto/webcrypto'\nimport { publicJwkToCryptoKeyAlgorithm, spkiToPublicJwk } from '../../crypto/webcrypto/utils'\nimport { TypedArrayEncoder } from '../../utils'\nimport { asymmetricPublicJwkMatches, PublicJwk } from '../kms'\nimport {\n convertName,\n createAuthorityKeyIdentifierExtension,\n createBasicConstraintsExtension,\n createCrlDistributionPointsExtension,\n createExtendedKeyUsagesExtension,\n createIssuerAlternativeNameExtension,\n createKeyUsagesExtension,\n createSubjectAlternativeNameExtension,\n createSubjectKeyIdentifierExtension,\n x509SignatureAlgorithmToJwa,\n} from './utils'\nimport { X509Error } from './X509Error'\nimport type { X509CreateCertificateOptions } from './X509ServiceOptions'\n\nexport enum X509KeyUsage {\n DigitalSignature = 1,\n NonRepudiation = 2,\n KeyEncipherment = 4,\n DataEncipherment = 8,\n KeyAgreement = 16,\n KeyCertSign = 32,\n CrlSign = 64,\n EncipherOnly = 128,\n DecipherOnly = 256,\n}\n\nexport enum X509ExtendedKeyUsage {\n ServerAuth = '1.3.6.1.5.5.7.3.1',\n ClientAuth = '1.3.6.1.5.5.7.3.2',\n CodeSigning = '1.3.6.1.5.5.7.3.3',\n EmailProtection = '1.3.6.1.5.5.7.3.4',\n TimeStamping = '1.3.6.1.5.5.7.3.8',\n OcspSigning = '1.3.6.1.5.5.7.3.9',\n MdlDs = '1.0.18013.5.1.2',\n}\n\nexport type X509CertificateOptions = {\n publicJwk: PublicJwk\n privateKey?: Uint8Array\n x509Certificate: x509.X509Certificate\n}\n\nexport class X509Certificate {\n public publicJwk: PublicJwk\n public privateKey?: Uint8Array\n private x509Certificate: x509.X509Certificate\n\n private constructor(options: X509CertificateOptions) {\n this.publicJwk = options.publicJwk\n this.privateKey = options.privateKey\n this.x509Certificate = options.x509Certificate\n }\n\n public set keyId(keyId: string) {\n this.publicJwk.keyId = keyId\n }\n\n public get keyId(): string {\n return this.publicJwk.keyId\n }\n\n public get hasKeyId(): boolean {\n return this.publicJwk.hasKeyId\n }\n\n public static fromRawCertificate(rawCertificate: Uint8Array): X509Certificate {\n const certificate = new x509.X509Certificate(rawCertificate)\n return X509Certificate.parseCertificate(certificate)\n }\n\n public static fromEncodedCertificate(encodedCertificate: string): X509Certificate {\n const certificate = new x509.X509Certificate(encodedCertificate)\n return X509Certificate.parseCertificate(certificate)\n }\n\n private static parseCertificate(certificate: x509.X509Certificate): X509Certificate {\n const spki = AsnParser.parse(certificate.publicKey.rawData, SubjectPublicKeyInfo)\n const privateKey = certificate.privateKey ? new Uint8Array(certificate.privateKey.rawData) : undefined\n\n const publicJwk = spkiToPublicJwk(spki)\n\n return new X509Certificate({\n publicJwk,\n privateKey,\n x509Certificate: certificate,\n })\n }\n\n private getMatchingExtensions<T = { critical: boolean }>(objectIdentifier: string): Array<T> | undefined {\n return this.x509Certificate.extensions.filter((e) => e.type === objectIdentifier) as Array<T> | undefined\n }\n\n public get rawCertificate() {\n return new Uint8Array(this.x509Certificate.rawData)\n }\n\n public get subjectAlternativeNames() {\n const san = this.getMatchingExtensions<x509.SubjectAlternativeNameExtension>(id_ce_subjectAltName)\n return san?.flatMap((s) => s.names.items).map((i) => ({ type: i.type, value: i.value })) ?? []\n }\n\n public get issuerAlternativeNames() {\n const ian = this.getMatchingExtensions<x509.IssuerAlternativeNameExtension>(id_ce_issuerAltName)\n return ian?.flatMap((i) => i.names.items).map((i) => ({ type: i.type, value: i.value })) ?? []\n }\n\n public get sanDnsNames() {\n return this.subjectAlternativeNames.filter((san) => san.type === 'dns').map((san) => san.value)\n }\n\n public get sanUriNames() {\n return this.subjectAlternativeNames.filter((ian) => ian.type === 'url').map((ian) => ian.value)\n }\n\n public get ianDnsNames() {\n return this.issuerAlternativeNames.filter((san) => san.type === 'dns').map((san) => san.value)\n }\n\n public get ianUriNames() {\n return this.issuerAlternativeNames.filter((ian) => ian.type === 'url').map((ian) => ian.value)\n }\n\n public get authorityKeyIdentifier() {\n const keyIds = this.getMatchingExtensions<x509.AuthorityKeyIdentifierExtension>(id_ce_authorityKeyIdentifier)?.map(\n (e) => e.keyId\n )\n\n if (keyIds && keyIds.length > 1) {\n throw new X509Error('Multiple Authority Key Identifiers are not allowed')\n }\n\n return keyIds?.[0]\n }\n\n public get subjectKeyIdentifier() {\n const keyIds = this.getMatchingExtensions<x509.SubjectKeyIdentifierExtension>(id_ce_subjectKeyIdentifier)?.map(\n (e) => e.keyId\n )\n\n if (keyIds && keyIds.length > 1) {\n throw new X509Error('Multiple Subject Key Identifiers are not allowed')\n }\n\n return keyIds?.[0]\n }\n\n // biome-ignore lint/suspicious/useGetterReturn: no explanation\n public get keyUsage() {\n const keyUsages = this.getMatchingExtensions<x509.KeyUsagesExtension>(id_ce_keyUsage)?.map((e) => e.usages)\n\n if (keyUsages && keyUsages.length > 1) {\n throw new X509Error('Multiple Key Usages are not allowed')\n }\n\n if (keyUsages) {\n return Object.values(X509KeyUsage)\n .filter((key): key is number => typeof key === 'number')\n .filter((flagValue) => (keyUsages[0] & flagValue) === flagValue)\n .map((flagValue) => flagValue as X509KeyUsage)\n }\n }\n\n public get extendedKeyUsage() {\n const extendedKeyUsages = this.getMatchingExtensions<x509.ExtendedKeyUsageExtension>(id_ce_extKeyUsage)?.map(\n (e) => e.usages\n )\n\n if (extendedKeyUsages && extendedKeyUsages.length > 1) {\n throw new X509Error('Multiple Key Usages are not allowed')\n }\n\n return (extendedKeyUsages?.[0] as Array<X509ExtendedKeyUsage> | undefined) ?? []\n }\n\n public isExtensionCritical(id: string): boolean {\n const extension = this.getMatchingExtensions(id)\n if (!extension) {\n throw new X509Error(`extension with id '${id}' is not found`)\n }\n\n return !!extension[0].critical\n }\n\n public static async create(options: X509CreateCertificateOptions, webCrypto: CredoWebCrypto) {\n const subjectPublicKey = options.subjectPublicKey ?? options.authorityKey\n const isSelfSignedCertificate = asymmetricPublicJwkMatches(options.authorityKey.toJson(), subjectPublicKey.toJson())\n\n const signingKey = new CredoWebCryptoKey(\n options.authorityKey,\n publicJwkToCryptoKeyAlgorithm(options.authorityKey),\n false,\n 'private',\n ['sign']\n )\n const publicKey = new CredoWebCryptoKey(\n subjectPublicKey,\n publicJwkToCryptoKeyAlgorithm(options.authorityKey),\n true,\n 'public',\n ['verify']\n )\n\n const issuerName = convertName(options.issuer)\n\n const extensions: Array<x509.Extension | undefined> = []\n extensions.push(\n createSubjectKeyIdentifierExtension(options.extensions?.subjectKeyIdentifier, { publicJwk: subjectPublicKey })\n )\n extensions.push(createKeyUsagesExtension(options.extensions?.keyUsage))\n extensions.push(createExtendedKeyUsagesExtension(options.extensions?.extendedKeyUsage))\n extensions.push(\n createAuthorityKeyIdentifierExtension(options.extensions?.authorityKeyIdentifier, {\n publicJwk: options.authorityKey,\n })\n )\n extensions.push(createIssuerAlternativeNameExtension(options.extensions?.issuerAlternativeName))\n extensions.push(createSubjectAlternativeNameExtension(options.extensions?.subjectAlternativeName))\n extensions.push(createBasicConstraintsExtension(options.extensions?.basicConstraints))\n extensions.push(createCrlDistributionPointsExtension(options.extensions?.crlDistributionPoints))\n\n if (isSelfSignedCertificate) {\n if (options.subject) {\n throw new X509Error('Do not provide a subject name when the certificate is supposed to be self signed')\n }\n\n const certificate = await x509.X509CertificateGenerator.createSelfSigned(\n {\n keys: { publicKey, privateKey: signingKey },\n name: issuerName,\n notBefore: options.validity?.notBefore,\n notAfter: options.validity?.notAfter,\n extensions: extensions.filter((e) => e !== undefined),\n serialNumber: options.serialNumber,\n },\n webCrypto\n )\n\n const certificateInstance = X509Certificate.parseCertificate(certificate)\n if (subjectPublicKey.hasKeyId) certificateInstance.publicJwk.keyId = subjectPublicKey.keyId\n return certificateInstance\n }\n\n if (!options.subject) {\n throw new X509Error('Provide a subject name when the certificate is not supposed to be self signed')\n }\n\n const subjectName = convertName(options.subject)\n\n const certificate = await x509.X509CertificateGenerator.create(\n {\n signingKey,\n publicKey,\n issuer: issuerName,\n subject: subjectName,\n notBefore: options.validity?.notBefore,\n notAfter: options.validity?.notAfter,\n extensions: extensions.filter((e) => e !== undefined),\n },\n webCrypto\n )\n\n const certificateInstance = X509Certificate.parseCertificate(certificate)\n if (subjectPublicKey.hasKeyId) certificateInstance.publicJwk.keyId = subjectPublicKey.keyId\n return certificateInstance\n }\n\n public get subject() {\n return this.x509Certificate.subject\n }\n\n public get issuer() {\n return this.x509Certificate.issuer\n }\n\n public async verify(\n {\n verificationDate = new Date(),\n publicJwk,\n skipSignatureVerification = false,\n }: {\n verificationDate: Date\n publicJwk?: PublicJwk\n\n /**\n * Whether to skip the verification of the signature and only perform other checks (such\n * as whether the certificate is not expired).\n *\n * This can be useful when an non-self-signed certificate is directly trusted, and it may\n * not be possible to verify the certificate as the root/intermediate certificate containing\n * the key of the signer/intermediate is not present.\n *\n * @default false\n */\n skipSignatureVerification?: boolean\n },\n webCrypto: CredoWebCrypto\n ) {\n let publicCryptoKey: CredoWebCryptoKey | undefined\n if (publicJwk) {\n const cryptoKeyAlgorithm = publicJwkToCryptoKeyAlgorithm(publicJwk, {\n alg: publicJwk.kty === 'RSA' ? x509SignatureAlgorithmToJwa(this.x509Certificate.signatureAlgorithm) : undefined,\n })\n\n publicCryptoKey = new CredoWebCryptoKey(publicJwk, cryptoKeyAlgorithm, true, 'public', ['verify'])\n }\n\n // We use the library to validate the signature, but the date is manually verified\n const isSignatureValid = skipSignatureVerification\n ? true\n : await this.x509Certificate.verify({ signatureOnly: true, publicKey: publicCryptoKey }, webCrypto)\n const time = verificationDate.getTime()\n\n const isNotBeforeValid = this.x509Certificate.notBefore.getTime() <= time\n const isNotAfterValid = time <= this.x509Certificate.notAfter.getTime()\n\n if (!isSignatureValid) {\n throw new X509Error(`Certificate: '${this.x509Certificate.subject}' has an invalid signature`)\n }\n\n if (!isNotBeforeValid) {\n throw new X509Error(`Certificate: '${this.x509Certificate.subject}' used before it is allowed`)\n }\n\n if (!isNotAfterValid) {\n throw new X509Error(`Certificate: '${this.x509Certificate.subject}' used after it is allowed`)\n }\n }\n\n /**\n * Get the thumbprint of the X509 certificate in hex format.\n */\n public async getThumbprintInHex(agentContext: AgentContext) {\n const thumbprint = await this.x509Certificate.getThumbprint(new CredoWebCrypto(agentContext))\n const thumbprintHex = TypedArrayEncoder.toHex(new Uint8Array(thumbprint))\n\n return thumbprintHex\n }\n\n /**\n * Get the data elements of the x509 certificate\n */\n public get data() {\n return {\n issuerName: this.x509Certificate.issuerName.toString(),\n issuer: this.x509Certificate.issuer,\n subjectName: this.x509Certificate.subjectName.toString(),\n subject: this.x509Certificate.subject,\n serialNumber: this.x509Certificate.serialNumber,\n pem: this.x509Certificate.toString(),\n notBefore: this.x509Certificate.notBefore,\n notAfter: this.x509Certificate.notAfter,\n }\n }\n\n public getIssuerNameField(field: string) {\n return this.x509Certificate.issuerName.getField(field)\n }\n\n public getSubjectNameField(field: string) {\n return this.x509Certificate.subjectName.getField(field)\n }\n\n /**\n * @param format the format to export to, defaults to `pem`\n */\n public toString(format?: 'asn' | 'pem' | 'hex' | 'base64' | 'text' | 'base64url') {\n return this.x509Certificate.toString(format ?? 'pem')\n }\n\n public equal(certificate: X509Certificate) {\n const parsedOther = new x509.X509Certificate(certificate.rawCertificate)\n\n return this.x509Certificate.equal(parsedOther)\n }\n}\n"],"mappings":"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"}