import * as asn1js from "asn1js"; import * as pvtsutils from "pvtsutils"; import * as pvutils from "pvutils"; import * as common from "./common"; import { AlgorithmIdentifier, AlgorithmIdentifierJson, AlgorithmIdentifierSchema } from "./AlgorithmIdentifier"; import { RelativeDistinguishedNames, RelativeDistinguishedNamesJson, RelativeDistinguishedNamesSchema } from "./RelativeDistinguishedNames"; import { Time, TimeJson, TimeSchema } from "./Time"; import { PublicKeyInfo, PublicKeyInfoJson, PublicKeyInfoSchema } from "./PublicKeyInfo"; import { Extension, ExtensionJson } from "./Extension"; import { Extensions, ExtensionsSchema } from "./Extensions"; import * as Schema from "./Schema"; import { id_BasicConstraints } from "./ObjectIdentifiers"; import { BasicConstraints } from "./BasicConstraints"; import { CryptoEnginePublicKeyParams } from "./CryptoEngine/CryptoEngineInterface"; import { AsnError } from "./errors"; import { PkiObject, PkiObjectParameters } from "./PkiObject"; import { EMPTY_BUFFER, EMPTY_STRING } from "./constants";
function tbsCertificate(parameters: TBSCertificateSchema = {}): Schema.SchemaType { //TBSCertificate ::= SEQUENCE { // version [0] EXPLICIT Version DEFAULT v1, // serialNumber CertificateSerialNumber, // signature AlgorithmIdentifier, // issuer Name, // validity Validity, // subject Name, // subjectPublicKeyInfo SubjectPublicKeyInfo, // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, // -- If present, version MUST be v2 or v3 // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, // -- If present, version MUST be v2 or v3 // extensions [3] EXPLICIT Extensions OPTIONAL // -- If present, version MUST be v3 //}
export interface ICertificate { /** * ToBeSigned (TBS) part of the certificate
*/
tbs: ArrayBuffer; /** * Version number
*/
version: number; /** * Serial number of the certificate
*/
serialNumber: asn1js.Integer; /** * This field contains the algorithm identifier for the algorithm used by the CA to sign the certificate
*/
signature: AlgorithmIdentifier; /** * The issuer field identifies the entity that has signed and issued the certificate
*/
issuer: RelativeDistinguishedNames; /** * The date on which the certificate validity period begins
*/
notBefore: Time; /** * The date on which the certificate validity period ends
*/
notAfter: Time; /** * The subject field identifies the entity associated with the public key stored in the subject public key field
*/
subject: RelativeDistinguishedNames; /** * This field is used to carry the public key and identify the algorithm with which the key is used
*/
subjectPublicKeyInfo: PublicKeyInfo; /** * The subject and issuer unique identifiers are present in the certificate to handle the possibility of reuse of subject and/or issuer names over time
*/
issuerUniqueID?: ArrayBuffer; /** * The subject and issuer unique identifiers are present in the certificate to handle the possibility of reuse of subject and/or issuer names over time
*/
subjectUniqueID?: ArrayBuffer; /** * If present, this field is a SEQUENCE of one or more certificate extensions
*/
extensions?: Extension[]; /** * The signatureAlgorithm field contains the identifier for the cryptographic algorithm used by the CA to sign this certificate
*/
signatureAlgorithm: AlgorithmIdentifier; /** * The signatureValue field contains a digital signature computed upon the ASN.1 DER encoded tbsCertificate
*/
signatureValue: asn1js.BitString;
}
/** * Constructor parameters for the {@link Certificate} class
*/
export type CertificateParameters = PkiObjectParameters & Partial<ICertificate>;
/** * Represents an X.509 certificate described in [RFC5280 Section 4](https://datatracker.ietf.org/doc/html/rfc5280#section-4). * * @example The following example demonstrates how to parse X.509 Certificate * ```js * const asn1 = asn1js.fromBER(raw); * if (asn1.offset === -1) { * throw new Error("Incorrect encoded ASN.1 data"); * } * * const cert = new pkijs.Certificate({ schema: asn1.result }); * ``` * * @example The following example demonstrates how to create self-signed certificate * ```js * const crypto = pkijs.getCrypto(true); * * // Create certificate * const certificate = new pkijs.Certificate(); * certificate.version = 2; * certificate.serialNumber = new asn1js.Integer({ value: 1 }); * certificate.issuer.typesAndValues.push(new pkijs.AttributeTypeAndValue({ * type: "2.5.4.3", // Common name * value: new asn1js.BmpString({ value: "Test" }) * })); * certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({ * type: "2.5.4.3", // Common name * value: new asn1js.BmpString({ value: "Test" }) * })); * * certificate.notBefore.value = new Date(); * const notAfter = new Date(); * notAfter.setUTCFullYear(notAfter.getUTCFullYear() + 1); * certificate.notAfter.value = notAfter; * * certificate.extensions = []; // Extensions are not a part of certificate by default, it's an optional array * * // "BasicConstraints" extension * const basicConstr = new pkijs.BasicConstraints({ * cA: true, * pathLenConstraint: 3 * }); * certificate.extensions.push(new pkijs.Extension({ * extnID: "2.5.29.19", * critical: false, * extnValue: basicConstr.toSchema().toBER(false), * parsedValue: basicConstr // Parsed value for well-known extensions * })); * * // "KeyUsage" extension * const bitArray = new ArrayBuffer(1); * const bitView = new Uint8Array(bitArray); * bitView[0] |= 0x02; // Key usage "cRLSign" flag * bitView[0] |= 0x04; // Key usage "keyCertSign" flag * const keyUsage = new asn1js.BitString({ valueHex: bitArray }); * certificate.extensions.push(new pkijs.Extension({ * extnID: "2.5.29.15", * critical: false, * extnValue: keyUsage.toBER(false), * parsedValue: keyUsage // Parsed value for well-known extensions * })); * * const algorithm = pkijs.getAlgorithmParameters("RSASSA-PKCS1-v1_5", "generateKey"); * if ("hash" in algorithm.algorithm) { * algorithm.algorithm.hash.name = "SHA-256"; * } * * const keys = await crypto.generateKey(algorithm.algorithm, true, algorithm.usages); * * // Exporting public key into "subjectPublicKeyInfo" value of certificate * await certificate.subjectPublicKeyInfo.importKey(keys.publicKey); * * // Signing final certificate * await certificate.sign(keys.privateKey, "SHA-256"); * * const raw = certificate.toSchema().toBER(); * ```
*/
export class Certificate extends PkiObject implements ICertificate {
publicstatic override CLASS_NAME = "Certificate";
public tbsView!: Uint8Array; /** * @deprecated Since version 3.0.0
*/ public get tbs(): ArrayBuffer { return pvtsutils.BufferSourceConverter.toArrayBuffer(this.tbsView);
}
/** * @deprecated Since version 3.0.0
*/ public set tbs(value: ArrayBuffer) { this.tbsView = new Uint8Array(value);
}
public version!: number; public serialNumber!: asn1js.Integer; public signature!: AlgorithmIdentifier; public issuer!: RelativeDistinguishedNames; public notBefore!: Time; public notAfter!: Time; public subject!: RelativeDistinguishedNames; public subjectPublicKeyInfo!: PublicKeyInfo; public issuerUniqueID?: ArrayBuffer; public subjectUniqueID?: ArrayBuffer; public extensions?: Extension[]; public signatureAlgorithm!: AlgorithmIdentifier; public signatureValue!: asn1js.BitString;
/** * Initializes a new instance of the {@link Certificate} class * @param parameters Initialization parameters
*/
constructor(parameters: CertificateParameters = {}) { super();
//#region Get internal properties from parsed schema this.tbsView = (asn1.result.tbsCertificate as asn1js.Sequence).valueBeforeDecodeView;
if (TBS_CERTIFICATE_VERSION in asn1.result) this.version = asn1.result[TBS_CERTIFICATE_VERSION].valueBlock.valueDec; this.serialNumber = asn1.result[TBS_CERTIFICATE_SERIAL_NUMBER]; this.signature = new AlgorithmIdentifier({ schema: asn1.result[TBS_CERTIFICATE_SIGNATURE] }); this.issuer = new RelativeDistinguishedNames({ schema: asn1.result[TBS_CERTIFICATE_ISSUER] }); this.notBefore = new Time({ schema: asn1.result[TBS_CERTIFICATE_NOT_BEFORE] }); this.notAfter = new Time({ schema: asn1.result[TBS_CERTIFICATE_NOT_AFTER] }); this.subject = new RelativeDistinguishedNames({ schema: asn1.result[TBS_CERTIFICATE_SUBJECT] }); this.subjectPublicKeyInfo = new PublicKeyInfo({ schema: asn1.result[TBS_CERTIFICATE_SUBJECT_PUBLIC_KEY] }); if (TBS_CERTIFICATE_ISSUER_UNIQUE_ID in asn1.result) this.issuerUniqueID = asn1.result[TBS_CERTIFICATE_ISSUER_UNIQUE_ID].valueBlock.valueHex; if (TBS_CERTIFICATE_SUBJECT_UNIQUE_ID in asn1.result) this.subjectUniqueID = asn1.result[TBS_CERTIFICATE_SUBJECT_UNIQUE_ID].valueBlock.valueHex; if (TBS_CERTIFICATE_EXTENSIONS in asn1.result) this.extensions = Array.from(asn1.result[TBS_CERTIFICATE_EXTENSIONS], element => new Extension({ schema: element }));
/** * Creates ASN.1 schema for existing values of TBS part for the certificate * @returns ASN.1 SEQUENCE
*/ public encodeTBS(): asn1js.Sequence { //#region Create array for output sequence const outputArray = [];
if ((VERSION in this) && (this.version !== Certificate.defaultValues(VERSION))) {
outputArray.push(new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0// [0]
},
value: [ new asn1js.Integer({ value: this.version }) // EXPLICIT integer value
]
}));
}
//#region Create and return output sequence return (new asn1js.Sequence({
value: outputArray
})); //#endregion
}
public toSchema(encodeFlag = false): asn1js.Sequence {
let tbsSchema: asn1js.AsnType;
// Decode stored TBS value if (encodeFlag === false) { if (!this.tbsView.byteLength) { // No stored certificate TBS part return Certificate.schema().value[0];
}
tbsSchema = asn1.result;
} else { // Create TBS schema via assembling from TBS parts
tbsSchema = this.encodeTBS();
}
// Construct and return new ASN.1 schema for this object return (new asn1js.Sequence({
value: [
tbsSchema, this.signatureAlgorithm.toSchema(), this.signatureValue
]
}));
}
if ((VERSION in this) && (this.version !== Certificate.defaultValues(VERSION))) {
res.version = this.version;
}
if (this.issuerUniqueID) {
res.issuerUniqueID = pvtsutils.Convert.ToHex(this.issuerUniqueID);
}
if (this.subjectUniqueID) {
res.subjectUniqueID = pvtsutils.Convert.ToHex(this.subjectUniqueID);
}
if (this.extensions) {
res.extensions = Array.from(this.extensions, o => o.toJSON());
}
return res;
}
/** * Importing public key for current certificate * @param parameters Public key export parameters * @param crypto Crypto engine * @returns WebCrypto public key
*/ public async getPublicKey(parameters?: CryptoEnginePublicKeyParams, crypto = common.getCrypto(true)): Promise<CryptoKey> { return crypto.getPublicKey(this.subjectPublicKeyInfo, this.signatureAlgorithm, parameters);
}
/** * Get hash value for subject public key (default SHA-1) * @param hashAlgorithm Hashing algorithm name * @param crypto Crypto engine * @returns Computed hash value from `Certificate.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey`
*/ public async getKeyHash(hashAlgorithm = "SHA-1", crypto = common.getCrypto(true)): Promise<ArrayBuffer> { return crypto.digest({ name: hashAlgorithm }, this.subjectPublicKeyInfo.subjectPublicKey.valueBlock.valueHexView);
}
/** * Make a signature for current value from TBS section * @param privateKey Private key for SUBJECT_PUBLIC_KEY_INFO structure * @param hashAlgorithm Hashing algorithm * @param crypto Crypto engine
*/ public async sign(privateKey: CryptoKey, hashAlgorithm = "SHA-1", crypto = common.getCrypto(true)): Promise<void> { // Initial checking if (!privateKey) { thrownew Error("Need to provide a private key for signing");
}
// Get a "default parameters" for current algorithm and set correct signature algorithm const signatureParameters = await crypto.getSignatureParameters(privateKey, hashAlgorithm); const parameters = signatureParameters.parameters; this.signature = signatureParameters.signatureAlgorithm; this.signatureAlgorithm = signatureParameters.signatureAlgorithm;
// Create TBS data for signing this.tbsView = new Uint8Array(this.encodeTBS().toBER());
// Signing TBS data on provided private key // TODO remove any const signature = await crypto.signWithPrivateKey(this.tbsView, privateKey, parameters as any); this.signatureValue = new asn1js.BitString({ valueHex: signature });
}
/** * Verifies the certificate signature * @param issuerCertificate * @param crypto Crypto engine
*/ public async verify(issuerCertificate?: Certificate, crypto = common.getCrypto(true)): Promise<boolean> {
let subjectPublicKeyInfo: PublicKeyInfo | undefined;
// Set correct SUBJECT_PUBLIC_KEY_INFO value if (issuerCertificate) {
subjectPublicKeyInfo = issuerCertificate.subjectPublicKeyInfo;
} elseif (this.issuer.isEqual(this.subject)) { // Self-signed certificate
subjectPublicKeyInfo = this.subjectPublicKeyInfo;
}
if (!(subjectPublicKeyInfo instanceof PublicKeyInfo)) { thrownew Error("Please provide issuer certificate as a parameter");
}
/** * Check CA flag for the certificate * @param cert Certificate to find CA flag for * @returns Returns {@link Certificate} if `cert` is CA certificate otherwise return `null`
*/
export function checkCA(cert: Certificate, signerCert: Certificate | null = null): Certificate | null { //#region Do not include signer's certificate if (signerCert && cert.issuer.isEqual(signerCert.issuer) && cert.serialNumber.isEqual(signerCert.serialNumber)) { returnnull;
} //#endregion
let isCA = false;
if (cert.extensions) { for (const extension of cert.extensions) { if (extension.extnID === id_BasicConstraints && extension.parsedValue instanceof BasicConstraints) { if (extension.parsedValue.cA) {
isCA = true; break;
}
}
}
}
if (isCA) { return cert;
}
returnnull;
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.28 Sekunden
(vorverarbeitet am 2026-06-06)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.