Examples
Every example is a complete script. Press Run to execute it in your browser with the current version of PKIjs, or copy it into your project.
Some examples take input: they run on sample data until you choose, drop, or paste your own files above the Run button. Results such as certificates and signed files can be downloaded after a run. Everything runs in your browser, and nothing is uploaded.
Certificates and revocation
- Create and validate a certificate: a root CA and an end-entity certificate, parsing, and chain validation.
- Working with certificate requests: create a PKCS#10 CSR and check it as a CA.
- Creating and parsing CRLs: revoke a certificate and check revocation.
- Working with OCSP requests: ask for a certificate's status.
- Working with OCSP responses: answer with a signed status and verify it.
Signing and encryption with CMS
- Working with CMS signing: sign data and verify the signature and its certificate chain.
- Working with CMS certificate-based encryption: encrypt for RSA and elliptic-curve certificates.
- Working with CMS password-based encryption: encrypt and decrypt with a password.
- Working with PKCS#7 certificate bags (P7B): bundle and list a certificate chain.
Timestamping
- Creating a timestamp request: ask a time-stamping authority for a timestamp.
- Creating a timestamp response: issue a timestamp and verify it.
Other
- Working with PKCS#12 files: store a key and its certificate in a password-protected file.
- S/MIME signature verification: verify a signed email, yours or the sample.
- S/MIME encryption: encrypt an email for a recipient's certificate.
- How to verify a signature in a PDF file: verify a signed PDF, yours or the sample.
The createIdentity helper
Most examples need certificates to work with. Instead of repeating the code of Create and validate a certificate in each of them, they import createIdentity from this file, which creates a key pair and a certificate, signed by an issuer or self-signed:
import * as asn1js from "asn1js";
import * as pkijs from "pkijs";
export interface Identity {
certificate: pkijs.Certificate;
keys: CryptoKeyPair;
}
export interface CreateIdentityParams {
/** Subject common name */
name: string;
/** Signs the certificate; when omitted, the certificate is self-signed */
issuer?: Identity;
/** Makes the certificate a CA that can sign certificates and CRLs */
ca?: boolean;
/** Extended key usage OIDs, such as "1.3.6.1.5.5.7.3.8" for timestamping */
extKeyUsage?: string[];
/** "RSA" creates an RSA key, which can also encrypt; "ECDSA" (the default) a P-256 key */
keyType?: "ECDSA" | "RSA";
}
let lastSerialNumber = 0;
/** Creates a key pair and a certificate for it, valid for one year */
export async function createIdentity({ name, issuer, ca = false, extKeyUsage, keyType = "ECDSA" }: CreateIdentityParams): Promise<Identity> {
const keys =
keyType === "RSA"
? await crypto.subtle.generateKey(
{ name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" },
true,
["sign", "verify"],
)
: await crypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, true, ["sign", "verify"]);
const certificate = new pkijs.Certificate();
certificate.version = 2;
certificate.serialNumber = new asn1js.Integer({ value: ++lastSerialNumber });
certificate.subject.typesAndValues.push(
new pkijs.AttributeTypeAndValue({
type: "2.5.4.3", // Common name
value: new asn1js.Utf8String({ value: name }),
}),
);
certificate.issuer = issuer ? issuer.certificate.subject : certificate.subject;
certificate.notBefore.value = new Date();
certificate.notAfter.value = new Date(Date.now() + 365 * 24 * 60 * 60 * 1000);
const basicConstraints = new pkijs.BasicConstraints({ cA: ca });
certificate.extensions = [
new pkijs.Extension({
extnID: pkijs.id_BasicConstraints,
critical: true,
extnValue: basicConstraints.toSchema().toBER(),
parsedValue: basicConstraints,
}),
];
if (extKeyUsage) {
const extKeyUsageValue = new pkijs.ExtKeyUsage({ keyPurposes: extKeyUsage });
certificate.extensions.push(
new pkijs.Extension({
extnID: pkijs.id_ExtKeyUsage,
critical: true,
extnValue: extKeyUsageValue.toSchema().toBER(),
parsedValue: extKeyUsageValue,
}),
);
}
await certificate.subjectPublicKeyInfo.importKey(keys.publicKey);
await certificate.sign((issuer ?? { keys }).keys.privateKey, "SHA-256");
return { certificate, keys };
}
/** Decodes base64, ignoring line breaks and other whitespace */
export function fromBase64(base64: string): Uint8Array<ArrayBuffer> {
return Uint8Array.from(atob(base64.replace(/\s+/g, "")), (char) => char.charCodeAt(0));
}
/** Encodes base64 in lines of 64 characters, as PEM does */
export function toBase64(data: BufferSource, lineLength = 64): string {
const bytes = ArrayBuffer.isView(data) ? new Uint8Array(data.buffer, data.byteOffset, data.byteLength) : new Uint8Array(data);
const base64 = btoa(Array.from(bytes, (byte) => String.fromCharCode(byte)).join(""));
return base64.match(new RegExp(`.{1,${lineLength}}`, "g"))?.join("\n") ?? "";
}
/** Encodes DER as PEM, such as "-----BEGIN CERTIFICATE-----" for the label "CERTIFICATE" */
export function toPEM(der: BufferSource, label: string): string {
return `-----BEGIN ${label}-----\n${toBase64(der)}\n-----END ${label}-----\n`;
}
/** Reads one DER certificate, or any number of PEM certificates, such as a CA bundle */
export function readCertificates(data: Uint8Array<ArrayBuffer>): pkijs.Certificate[] {
const text = new TextDecoder().decode(data);
if (!text.includes("-----BEGIN CERTIFICATE-----")) {
return [pkijs.Certificate.fromBER(data)];
}
const blocks = text.matchAll(/-----BEGIN CERTIFICATE-----([^-]+)-----END CERTIFICATE-----/g);
return Array.from(blocks, ([, base64]) => pkijs.Certificate.fromBER(fromBase64(base64)));
}
The examples also import file, text, and download from io.ts, which connect them to the controls on this website. When you copy an example, replace its inputs with your own data, and download() with saving or sending the result.