@noz-ele/edgca 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +109 -2
- package/dist/ca.d.ts +2 -1
- package/dist/ca.d.ts.map +1 -1
- package/dist/ca.js +46 -22
- package/dist/cli/commands/create-root-ca.d.ts +2 -0
- package/dist/cli/commands/create-root-ca.d.ts.map +1 -0
- package/dist/cli/commands/create-root-ca.js +26 -0
- package/dist/cli/commands/issue-client.d.ts +2 -0
- package/dist/cli/commands/issue-client.d.ts.map +1 -0
- package/dist/cli/commands/issue-client.js +46 -0
- package/dist/cli/commands/issue-intermediate-ca.d.ts +2 -0
- package/dist/cli/commands/issue-intermediate-ca.d.ts.map +1 -0
- package/dist/cli/commands/issue-intermediate-ca.js +33 -0
- package/dist/cli/commands/pem-to-pfx.d.ts +2 -0
- package/dist/cli/commands/pem-to-pfx.d.ts.map +1 -0
- package/dist/cli/commands/pem-to-pfx.js +47 -0
- package/dist/cli/dn.d.ts +3 -0
- package/dist/cli/dn.d.ts.map +1 -0
- package/dist/cli/dn.js +65 -0
- package/dist/cli/flags.d.ts +8 -0
- package/dist/cli/flags.d.ts.map +1 -0
- package/dist/cli/flags.js +32 -0
- package/dist/cli/io.d.ts +19 -0
- package/dist/cli/io.d.ts.map +1 -0
- package/dist/cli/io.js +92 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +52 -0
- package/dist/csr.d.ts +12 -0
- package/dist/csr.d.ts.map +1 -1
- package/dist/csr.js +52 -8
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/oids.d.ts +1 -0
- package/dist/oids.d.ts.map +1 -1
- package/dist/oids.js +1 -0
- package/dist/pem.d.ts +2 -0
- package/dist/pem.d.ts.map +1 -1
- package/dist/pem.js +4 -1
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/x509.d.ts +3 -0
- package/dist/x509.d.ts.map +1 -1
- package/dist/x509.js +5 -1
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> [日本語](https://github.com/noz-ele/EdgCA/blob/main/docs/jp/README.md) | English
|
|
4
4
|
|
|
5
|
-
EdgCA is a small TypeScript library that issues mTLS client certificates from a self-managed CA on Cloudflare Workers-compatible runtimes. It supports internal keygen, CSR-based enrollment (PKCS#10 + proof-of-possession), and PFX (PKCS#12) export for OS keystore import.
|
|
5
|
+
EdgCA is a small TypeScript library that issues mTLS client certificates and document-signing certificates from a self-managed CA on Cloudflare Workers-compatible runtimes. It supports internal keygen, CSR-based enrollment for mTLS leaves (PKCS#10 + proof-of-possession), and PFX (PKCS#12) export for OS keystore import.
|
|
6
6
|
|
|
7
7
|
The scope is intentionally narrow:
|
|
8
8
|
|
|
@@ -10,6 +10,7 @@ The scope is intentionally narrow:
|
|
|
10
10
|
- Issue an intermediate CA from a root CA.
|
|
11
11
|
- Issue an mTLS client certificate and private key from an intermediate CA.
|
|
12
12
|
- Issue an mTLS client certificate from a caller-provided public key (no private key returned). Pairs with the CSR helpers below.
|
|
13
|
+
- Issue a document-signing certificate (RFC 9336 `id-kp-documentSigning`) and private key from a CA. Used as the signer cert for CAdES / CMS / ASiC-style document signatures, which are produced by separate tooling.
|
|
13
14
|
- Parse a PKCS#10 CSR (subject, requested SAN, public key, raw extensions/attributes) and verify its proof-of-possession signature.
|
|
14
15
|
- Decide whether a received client certificate was issued by your own CA.
|
|
15
16
|
- Encode/decode certificates as PEM/DER. Keys are exchanged as `CryptoKey` only — the library never returns or accepts string forms (PEM, JWK, etc.) of private keys.
|
|
@@ -22,7 +23,9 @@ ECDSA on **NIST P-256, P-384, and P-521** is supported throughout (signing, veri
|
|
|
22
23
|
|
|
23
24
|
## Contents
|
|
24
25
|
|
|
26
|
+
- [CLI](#cli) — `npx edgca …` one-liners for the four most common tasks
|
|
25
27
|
- [Quick Start](#quick-start) — root → intermediate → client cert (incl. PFX bundling)
|
|
28
|
+
- [Issue a document-signing certificate](#issue-a-document-signing-certificate) — RFC 9336 `id-kp-documentSigning` leaf
|
|
26
29
|
- [Verify on Cloudflare Worker](#verify-cloudflare-worker) — confirm a cert was issued by your CA
|
|
27
30
|
- [Issue from a CSR](#issue-from-a-csr) — accept a caller-managed key via PKCS#10 + POP
|
|
28
31
|
- [Subject](#subject) · [Scope](#scope) · [Key Handling](#key-handling) · [Development](#development) · [API Documentation](#api-documentation)
|
|
@@ -39,6 +42,66 @@ npm install @noz-ele/edgca
|
|
|
39
42
|
|
|
40
43
|
ESM-only (`"type": "module"`). Runs on any runtime where `globalThis.crypto.subtle` is available (Cloudflare Workers, Node.js 20+, modern browsers, etc.). CommonJS `require` is not supported.
|
|
41
44
|
|
|
45
|
+
## CLI
|
|
46
|
+
|
|
47
|
+
EdgCA ships a small zero-dependency CLI (`bin: edgca`) for the four most common one-shot tasks. It is a thin wrapper over the library API and uses `node:util.parseArgs` — no transitive dependencies are pulled in for non-CLI consumers.
|
|
48
|
+
|
|
49
|
+
> `npx` runs the CLI **without installing** it — it fetches the package into npm's local cache, executes the `bin`, and leaves your project's `node_modules` / `package.json` untouched. Use this for one-shot tasks (creating a local dev CA, building a PFX). For repeated use, install globally with `npm install -g @noz-ele/edgca` and then call `edgca …` directly.
|
|
50
|
+
|
|
51
|
+
All commands write outputs to the **current working directory**. Filenames are derived from `--name` (default: `root` / `intermediate` / `client`). Cert files are `<name>.crt.pem`, private keys are PKCS#8 PEM at `<name>.key.pem`, and full chains (when relevant) are `<name>.chain.pem`.
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
# 1. Create a root CA (default: P-256, 3650 days)
|
|
55
|
+
npx @noz-ele/edgca create-root-ca --subject "CN=My Test Root,O=Acme,C=JP"
|
|
56
|
+
# → ./root.crt.pem, ./root.key.pem
|
|
57
|
+
|
|
58
|
+
# 2. Issue an intermediate CA from that root
|
|
59
|
+
npx @noz-ele/edgca issue-intermediate-ca \
|
|
60
|
+
--ca-cert root.crt.pem --ca-key root.key.pem \
|
|
61
|
+
--subject "CN=My Intermediate,O=Acme"
|
|
62
|
+
# → ./intermediate.crt.pem, ./intermediate.key.pem, ./intermediate.chain.pem
|
|
63
|
+
|
|
64
|
+
# 3. Issue an mTLS client cert from the intermediate
|
|
65
|
+
npx @noz-ele/edgca issue-client \
|
|
66
|
+
--ca-cert intermediate.crt.pem --ca-key intermediate.key.pem \
|
|
67
|
+
--ca-chain intermediate.chain.pem \
|
|
68
|
+
--subject "CN=alice" \
|
|
69
|
+
--dns-name alice.example.test --ip 10.0.0.1 \
|
|
70
|
+
--days 365
|
|
71
|
+
# → ./client.crt.pem, ./client.key.pem, ./client.chain.pem
|
|
72
|
+
|
|
73
|
+
# 4. Bundle a cert + key (+ optional chain) into a password-protected PFX
|
|
74
|
+
npx @noz-ele/edgca pem-to-pfx \
|
|
75
|
+
--cert client.crt.pem --key client.key.pem --chain client.chain.pem \
|
|
76
|
+
--password "hunter2"
|
|
77
|
+
# → ./client.pfx (defaults to <cert-basename>.pfx next to --cert)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Flags summary (run `npx @noz-ele/edgca --help` to see this in your terminal):
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
edgca create-root-ca --subject <dn>
|
|
84
|
+
[--days 3650] [--curve P-256|P-384|P-521]
|
|
85
|
+
[--name root]
|
|
86
|
+
|
|
87
|
+
edgca issue-intermediate-ca --ca-cert <pem> --ca-key <pem>
|
|
88
|
+
--subject <dn>
|
|
89
|
+
[--days 1825] [--curve P-256|P-384|P-521]
|
|
90
|
+
[--name intermediate]
|
|
91
|
+
|
|
92
|
+
edgca issue-client --ca-cert <pem> --ca-key <pem> [--ca-chain <pem>]
|
|
93
|
+
--subject <dn>
|
|
94
|
+
[--dns-name <name>]... [--ip <addr>]...
|
|
95
|
+
[--days 365] [--name client]
|
|
96
|
+
|
|
97
|
+
edgca pem-to-pfx --cert <pem> --key <pem> --password <pw>
|
|
98
|
+
[--chain <pem>] [--out <pfx>]
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`--subject` accepts an OpenSSL-style DN string (`"CN=foo,O=bar,C=JP"`); short names are case-insensitive (`CN`/`O`/`OU`/`C`/`ST`/`L`/`E`/`DC`/`SERIALNUMBER`/`STREET`/`POSTALCODE`/`TITLE`/`GIVENNAME`/`SURNAME`/`UID`), and dotted OID attributes (`1.2.840.113549.1.9.1=...`) are accepted as-is. Private keys are read/written as PKCS#8 PEM (`-----BEGIN PRIVATE KEY-----`); SEC1 (`EC PRIVATE KEY`) is not supported.
|
|
102
|
+
|
|
103
|
+
> ⚠ The CLI is a convenience wrapper for local testing and one-shot operational tasks. For programmatic use in a server, Worker, or browser, call the library API directly — that way private keys stay in `CryptoKey` form and never touch disk. See [Key Handling](#key-handling) for the rationale.
|
|
104
|
+
|
|
42
105
|
## Quick Start
|
|
43
106
|
|
|
44
107
|
```ts
|
|
@@ -119,6 +182,46 @@ The implementation is environment-agnostic (WebCrypto only, no Node-specific API
|
|
|
119
182
|
|
|
120
183
|
The `@noz-ele/edgca/pkcs12` subpath is provided so consumers that only need PFX assembly can import it without pulling in the CA / CSR / verify modules.
|
|
121
184
|
|
|
185
|
+
## Issue a document-signing certificate
|
|
186
|
+
|
|
187
|
+
`issueDocumentSigningCert` issues a leaf intended for signing arbitrary documents (CAdES detached, CMS, ASiC-E containers, etc., which are produced by separate tooling — not by EdgCA). It is **not** an mTLS client certificate: the EKU is `id-kp-documentSigning` (RFC 9336), and `keyUsage` carries `digitalSignature, contentCommitment` instead of just `digitalSignature`. SAN is intentionally not accepted; the signer is identified by Subject DN.
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
import {
|
|
191
|
+
createRootCA,
|
|
192
|
+
issueIntermediateCA,
|
|
193
|
+
issueDocumentSigningCert
|
|
194
|
+
} from "@noz-ele/edgca";
|
|
195
|
+
|
|
196
|
+
const root = await createRootCA({
|
|
197
|
+
subject: [{ type: "CN", value: "dev-root" }],
|
|
198
|
+
days: 3650
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const intermediate = await issueIntermediateCA({
|
|
202
|
+
ca: root,
|
|
203
|
+
subject: [{ type: "CN", value: "dev-intermediate" }],
|
|
204
|
+
days: 365
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
const signer = await issueDocumentSigningCert({
|
|
208
|
+
ca: intermediate,
|
|
209
|
+
subject: [
|
|
210
|
+
{ type: "CN", value: "Alice (Document Signer)" },
|
|
211
|
+
{ type: "O", value: "Example" }
|
|
212
|
+
],
|
|
213
|
+
days: 365
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// signer.certPem — the signing certificate
|
|
217
|
+
// signer.certChainPem — full chain to embed alongside the signature (signer + intermediate + root)
|
|
218
|
+
// signer.privateKey — secret CryptoKey, used by the document-signing tool to produce the signature
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
The returned shape is `IssuedDocumentSigningCertificate` (structurally identical to `IssuedClientCertificate`; the distinct name flags the EKU profile). The same `exportPkcs12` flow used for mTLS leaves works here too if you need to bundle the signer cert + key into a PFX for tools that consume PKCS#12.
|
|
222
|
+
|
|
223
|
+
There is no `issueDocumentSigningCertForPublicKey` (CSR variant) in v1, and EdgCA does not produce CAdES / CMS / ASiC containers itself — see [docs/en/NON_GOALS.md](https://github.com/noz-ele/EdgCA/blob/main/docs/en/NON_GOALS.md) for the rationale.
|
|
224
|
+
|
|
122
225
|
## Verify (Cloudflare Worker)
|
|
123
226
|
|
|
124
227
|
> ⚠ **What this is — and is not**
|
|
@@ -283,6 +386,7 @@ In scope:
|
|
|
283
386
|
- Root CA creation.
|
|
284
387
|
- Intermediate CA issuance.
|
|
285
388
|
- mTLS client certificate issuance (with internal key generation, or from a caller-provided public key).
|
|
389
|
+
- Document-signing certificate issuance with EKU `id-kp-documentSigning` (RFC 9336) and `keyUsage digitalSignature, contentCommitment` — internal key generation only, no SAN.
|
|
286
390
|
- CSR (PKCS#10) parsing and proof-of-possession signature verification.
|
|
287
391
|
- Identity check that a cert was issued by your own CA (`verifyClientCertificateIssuedBy`, with optional time-validity check).
|
|
288
392
|
- PEM/DER helpers (certificates only — keys are exchanged as `CryptoKey`).
|
|
@@ -291,7 +395,10 @@ In scope:
|
|
|
291
395
|
|
|
292
396
|
Intentionally out of scope:
|
|
293
397
|
|
|
294
|
-
- Server certificate issuance.
|
|
398
|
+
- Server certificate issuance. Leaf scope is mTLS client certs and document-signing certs only.
|
|
399
|
+
- Document-signing certificate issuance from a caller-provided public key (`issueDocumentSigningCertForPublicKey`) — not in v1.
|
|
400
|
+
- SAN (`dnsNames` / `ipAddresses` / `emailAddresses`) on document-signing leaves.
|
|
401
|
+
- CAdES / CMS / PAdES / XAdES / ASiC document signing or container building. EdgCA only issues the signing certificate; producing a signed document or container is a separate concern.
|
|
295
402
|
- Public chain-validation APIs.
|
|
296
403
|
- Extracting time fields from a cert. `verifyClientCertificateIssuedBy`'s `validity` option performs the time check, but the `notBefore` / `notAfter` values are passed in by the caller from `cf.tlsClientAuth`.
|
|
297
404
|
- CRL, OCSP, revocation databases, revocation checks.
|
package/dist/ca.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type { CertificateAuthority, CreateRootCAOptions, ImportCertificateAuthorityOptions, IssueClientCertForPublicKeyOptions, IssueClientCertOptions, IssueIntermediateCAOptions, IssuedClientCertificate, IssuedClientCertificateForPublicKey } from "./types.js";
|
|
1
|
+
import type { CertificateAuthority, CreateRootCAOptions, ImportCertificateAuthorityOptions, IssueClientCertForPublicKeyOptions, IssueClientCertOptions, IssueDocumentSigningCertOptions, IssueIntermediateCAOptions, IssuedClientCertificate, IssuedClientCertificateForPublicKey, IssuedDocumentSigningCertificate } from "./types.js";
|
|
2
2
|
export declare function createRootCA(options: CreateRootCAOptions): Promise<CertificateAuthority>;
|
|
3
3
|
export declare function issueIntermediateCA(options: IssueIntermediateCAOptions): Promise<CertificateAuthority>;
|
|
4
4
|
export declare function issueClientCert(options: IssueClientCertOptions): Promise<IssuedClientCertificate>;
|
|
5
5
|
export declare function issueClientCertForPublicKey(options: IssueClientCertForPublicKeyOptions): Promise<IssuedClientCertificateForPublicKey>;
|
|
6
|
+
export declare function issueDocumentSigningCert(options: IssueDocumentSigningCertOptions): Promise<IssuedDocumentSigningCertificate>;
|
|
6
7
|
export declare function importCertificateAuthority(options: ImportCertificateAuthorityOptions): Promise<CertificateAuthority>;
|
|
7
8
|
//# sourceMappingURL=ca.d.ts.map
|
package/dist/ca.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ca.d.ts","sourceRoot":"","sources":["../src/ca.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,oBAAoB,EACpB,mBAAmB,EACnB,iCAAiC,EACjC,kCAAkC,EAClC,sBAAsB,EACtB,0BAA0B,EAC1B,uBAAuB,EACvB,mCAAmC,
|
|
1
|
+
{"version":3,"file":"ca.d.ts","sourceRoot":"","sources":["../src/ca.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,oBAAoB,EACpB,mBAAmB,EACnB,iCAAiC,EACjC,kCAAkC,EAClC,sBAAsB,EACtB,+BAA+B,EAC/B,0BAA0B,EAC1B,uBAAuB,EACvB,mCAAmC,EACnC,gCAAgC,EAEjC,MAAM,YAAY,CAAC;AAcpB,wBAAsB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA0B9F;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAgC5G;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAoBvG;AAED,wBAAsB,2BAA2B,CAC/C,OAAO,EAAE,kCAAkC,GAC1C,OAAO,CAAC,mCAAmC,CAAC,CAiB9C;AAuED,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,gCAAgC,CAAC,CAoB3C;AAED,wBAAsB,0BAA0B,CAAC,OAAO,EAAE,iCAAiC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAe1H"}
|
package/dist/ca.js
CHANGED
|
@@ -3,7 +3,7 @@ import { assertKeyPairMatches, curveOf, exportSpki, generateKeyPair, keyIdentifi
|
|
|
3
3
|
import { encodeName } from "./name.js";
|
|
4
4
|
import { certificateToPem, pemToDer, pemToDerWithLabel, splitPemBlocks } from "./pem.js";
|
|
5
5
|
import { parseCertificateDer } from "./parser.js";
|
|
6
|
-
import { authorityKeyIdentifierExtension, basicConstraintsCaExtension, basicConstraintsLeafExtension, buildCertificate, buildTbsCertificate, extendedKeyUsageClientAuthExtension, keyUsageExtension, subjectAltNameExtension, subjectKeyIdentifierExtension } from "./x509.js";
|
|
6
|
+
import { authorityKeyIdentifierExtension, basicConstraintsCaExtension, basicConstraintsLeafExtension, buildCertificate, buildTbsCertificate, extendedKeyUsageClientAuthExtension, extendedKeyUsageDocumentSigningExtension, keyUsageExtension, subjectAltNameExtension, subjectKeyIdentifierExtension } from "./x509.js";
|
|
7
7
|
export async function createRootCA(options) {
|
|
8
8
|
const keyPair = await resolveKeyPair(options.keyPair);
|
|
9
9
|
const issuerCurve = curveOf(keyPair.privateKey);
|
|
@@ -65,14 +65,12 @@ export async function issueIntermediateCA(options) {
|
|
|
65
65
|
}
|
|
66
66
|
export async function issueClientCert(options) {
|
|
67
67
|
const keyPair = await generateKeyPair();
|
|
68
|
-
const built = await
|
|
68
|
+
const built = await buildLeafCertificate(options.ca, keyPair.publicKey, {
|
|
69
69
|
subject: options.subject,
|
|
70
70
|
days: options.days,
|
|
71
71
|
notBefore: options.notBefore,
|
|
72
|
-
serialNumber: options.serialNumber
|
|
73
|
-
|
|
74
|
-
ipAddresses: options.ipAddresses
|
|
75
|
-
});
|
|
72
|
+
serialNumber: options.serialNumber
|
|
73
|
+
}, clientCertProfileExtensions(options.dnsNames, options.ipAddresses));
|
|
76
74
|
return {
|
|
77
75
|
certPem: built.certPem,
|
|
78
76
|
certDer: built.certDer,
|
|
@@ -82,42 +80,36 @@ export async function issueClientCert(options) {
|
|
|
82
80
|
};
|
|
83
81
|
}
|
|
84
82
|
export async function issueClientCertForPublicKey(options) {
|
|
85
|
-
const built = await
|
|
83
|
+
const built = await buildLeafCertificate(options.ca, options.publicKey, {
|
|
86
84
|
subject: options.subject,
|
|
87
85
|
days: options.days,
|
|
88
86
|
notBefore: options.notBefore,
|
|
89
|
-
serialNumber: options.serialNumber
|
|
90
|
-
|
|
91
|
-
ipAddresses: options.ipAddresses
|
|
92
|
-
});
|
|
87
|
+
serialNumber: options.serialNumber
|
|
88
|
+
}, clientCertProfileExtensions(options.dnsNames, options.ipAddresses));
|
|
93
89
|
return {
|
|
94
90
|
certPem: built.certPem,
|
|
95
91
|
certDer: built.certDer,
|
|
96
92
|
certChainPem: built.certChainPem
|
|
97
93
|
};
|
|
98
94
|
}
|
|
99
|
-
async function
|
|
95
|
+
async function buildLeafCertificate(ca, subjectPublicKey, identity, profileExtensions) {
|
|
100
96
|
const issuer = await parseIssuer(ca);
|
|
101
97
|
const issuerChainPem = ca.issuerChainPem;
|
|
102
98
|
assertCanIssueCertificate(issuer);
|
|
103
99
|
const issuerCurve = curveOf(ca.privateKey);
|
|
104
|
-
const subjectNameDer = encodeName(
|
|
100
|
+
const subjectNameDer = encodeName(identity.subject);
|
|
105
101
|
const spki = await exportSpki(subjectPublicKey);
|
|
106
102
|
const subjectKeyIdentifier = await keyIdentifierFromSpki(spki);
|
|
107
103
|
const authorityKeyIdentifier = issuer.subjectKeyIdentifier ?? await keyIdentifierFromSpki(issuer.subjectPublicKeyInfoDer);
|
|
108
|
-
const san = subjectAltNameExtension(content.dnsNames, content.ipAddresses);
|
|
109
104
|
const extensions = [
|
|
110
|
-
|
|
111
|
-
keyUsageExtension(["digitalSignature"]),
|
|
112
|
-
extendedKeyUsageClientAuthExtension(),
|
|
105
|
+
...profileExtensions,
|
|
113
106
|
subjectKeyIdentifierExtension(subjectKeyIdentifier),
|
|
114
|
-
authorityKeyIdentifierExtension(authorityKeyIdentifier)
|
|
115
|
-
...(san ? [san] : [])
|
|
107
|
+
authorityKeyIdentifierExtension(authorityKeyIdentifier)
|
|
116
108
|
];
|
|
117
109
|
const { tbsCertificateDer } = buildTbsCertificate({
|
|
118
|
-
serialNumber:
|
|
119
|
-
notBefore:
|
|
120
|
-
days:
|
|
110
|
+
serialNumber: identity.serialNumber,
|
|
111
|
+
notBefore: identity.notBefore,
|
|
112
|
+
days: identity.days,
|
|
121
113
|
issuerNameDer: issuer.subjectNameDer,
|
|
122
114
|
subjectNameDer,
|
|
123
115
|
subjectPublicKeyInfoDer: spki,
|
|
@@ -133,6 +125,38 @@ async function buildClientCertificate(ca, subjectPublicKey, content) {
|
|
|
133
125
|
certChainPem: joinPemChain([certPem, ca.certPem, issuerChainPem])
|
|
134
126
|
};
|
|
135
127
|
}
|
|
128
|
+
function clientCertProfileExtensions(dnsNames, ipAddresses) {
|
|
129
|
+
const san = subjectAltNameExtension(dnsNames, ipAddresses);
|
|
130
|
+
return [
|
|
131
|
+
basicConstraintsLeafExtension(),
|
|
132
|
+
keyUsageExtension(["digitalSignature"]),
|
|
133
|
+
extendedKeyUsageClientAuthExtension(),
|
|
134
|
+
...(san ? [san] : [])
|
|
135
|
+
];
|
|
136
|
+
}
|
|
137
|
+
function documentSigningProfileExtensions() {
|
|
138
|
+
return [
|
|
139
|
+
basicConstraintsLeafExtension(),
|
|
140
|
+
keyUsageExtension(["digitalSignature", "contentCommitment"]),
|
|
141
|
+
extendedKeyUsageDocumentSigningExtension()
|
|
142
|
+
];
|
|
143
|
+
}
|
|
144
|
+
export async function issueDocumentSigningCert(options) {
|
|
145
|
+
const keyPair = await generateKeyPair();
|
|
146
|
+
const built = await buildLeafCertificate(options.ca, keyPair.publicKey, {
|
|
147
|
+
subject: options.subject,
|
|
148
|
+
days: options.days,
|
|
149
|
+
notBefore: options.notBefore,
|
|
150
|
+
serialNumber: options.serialNumber
|
|
151
|
+
}, documentSigningProfileExtensions());
|
|
152
|
+
return {
|
|
153
|
+
certPem: built.certPem,
|
|
154
|
+
certDer: built.certDer,
|
|
155
|
+
privateKey: keyPair.privateKey,
|
|
156
|
+
publicKey: keyPair.publicKey,
|
|
157
|
+
certChainPem: built.certChainPem
|
|
158
|
+
};
|
|
159
|
+
}
|
|
136
160
|
export async function importCertificateAuthority(options) {
|
|
137
161
|
const issuerChainPem = options.issuerChainPem ?? "";
|
|
138
162
|
assertIssuerChainPem(issuerChainPem);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-root-ca.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/create-root-ca.ts"],"names":[],"mappings":"AAMA,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBvE"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
import { createRootCA, generateKeyPair } from "../../index.js";
|
|
3
|
+
import { parseDnString } from "../dn.js";
|
|
4
|
+
import { parseCurveFlag, parseDaysFlag, requireString } from "../flags.js";
|
|
5
|
+
import { writeCaTriplet } from "../io.js";
|
|
6
|
+
export async function createRootCaCommand(args) {
|
|
7
|
+
const { values } = parseArgs({
|
|
8
|
+
args,
|
|
9
|
+
options: {
|
|
10
|
+
subject: { type: "string" },
|
|
11
|
+
days: { type: "string", default: "3650" },
|
|
12
|
+
curve: { type: "string", default: "P-256" },
|
|
13
|
+
name: { type: "string", default: "root" }
|
|
14
|
+
},
|
|
15
|
+
strict: true
|
|
16
|
+
});
|
|
17
|
+
const subject = parseDnString(requireString(values.subject, "--subject"));
|
|
18
|
+
const days = parseDaysFlag(values.days);
|
|
19
|
+
const curve = parseCurveFlag(values.curve);
|
|
20
|
+
const basename = values.name;
|
|
21
|
+
const keyPair = await generateKeyPair(curve);
|
|
22
|
+
const ca = await createRootCA({ subject, days, keyPair });
|
|
23
|
+
const written = await writeCaTriplet(ca, ".", basename);
|
|
24
|
+
console.log(`wrote ${written.certPath}`);
|
|
25
|
+
console.log(`wrote ${written.keyPath}`);
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"issue-client.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/issue-client.ts"],"names":[],"mappings":"AAMA,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA2CtE"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
import { issueClientCert } from "../../index.js";
|
|
3
|
+
import { parseDnString } from "../dn.js";
|
|
4
|
+
import { parseDaysFlag, requireString } from "../flags.js";
|
|
5
|
+
import { loadCaFromDisk, writeIssuedLeafTriplet } from "../io.js";
|
|
6
|
+
export async function issueClientCommand(args) {
|
|
7
|
+
const { values } = parseArgs({
|
|
8
|
+
args,
|
|
9
|
+
options: {
|
|
10
|
+
"ca-cert": { type: "string" },
|
|
11
|
+
"ca-key": { type: "string" },
|
|
12
|
+
"ca-chain": { type: "string" },
|
|
13
|
+
subject: { type: "string" },
|
|
14
|
+
"dns-name": { type: "string", multiple: true, default: [] },
|
|
15
|
+
ip: { type: "string", multiple: true, default: [] },
|
|
16
|
+
days: { type: "string", default: "365" },
|
|
17
|
+
name: { type: "string", default: "client" }
|
|
18
|
+
},
|
|
19
|
+
strict: true
|
|
20
|
+
});
|
|
21
|
+
const caCertPath = requireString(values["ca-cert"], "--ca-cert");
|
|
22
|
+
const caKeyPath = requireString(values["ca-key"], "--ca-key");
|
|
23
|
+
const caChainPath = values["ca-chain"];
|
|
24
|
+
const subject = parseDnString(requireString(values.subject, "--subject"));
|
|
25
|
+
const days = parseDaysFlag(values.days);
|
|
26
|
+
const basename = values.name;
|
|
27
|
+
const dnsNames = values["dns-name"];
|
|
28
|
+
const ipAddresses = values.ip;
|
|
29
|
+
const ca = await loadCaFromDisk({
|
|
30
|
+
certPath: caCertPath,
|
|
31
|
+
keyPath: caKeyPath,
|
|
32
|
+
...(caChainPath !== undefined ? { chainPath: caChainPath } : {})
|
|
33
|
+
});
|
|
34
|
+
const issued = await issueClientCert({
|
|
35
|
+
ca,
|
|
36
|
+
subject,
|
|
37
|
+
days,
|
|
38
|
+
...(dnsNames.length > 0 ? { dnsNames } : {}),
|
|
39
|
+
...(ipAddresses.length > 0 ? { ipAddresses } : {})
|
|
40
|
+
});
|
|
41
|
+
const written = await writeIssuedLeafTriplet(issued, ".", basename);
|
|
42
|
+
console.log(`wrote ${written.certPath}`);
|
|
43
|
+
console.log(`wrote ${written.keyPath}`);
|
|
44
|
+
if (written.chainPath)
|
|
45
|
+
console.log(`wrote ${written.chainPath}`);
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"issue-intermediate-ca.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/issue-intermediate-ca.ts"],"names":[],"mappings":"AAMA,wBAAsB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA6B9E"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
import { generateKeyPair, issueIntermediateCA } from "../../index.js";
|
|
3
|
+
import { parseDnString } from "../dn.js";
|
|
4
|
+
import { parseCurveFlag, parseDaysFlag, requireString } from "../flags.js";
|
|
5
|
+
import { loadCaFromDisk, writeCaTriplet } from "../io.js";
|
|
6
|
+
export async function issueIntermediateCaCommand(args) {
|
|
7
|
+
const { values } = parseArgs({
|
|
8
|
+
args,
|
|
9
|
+
options: {
|
|
10
|
+
"ca-cert": { type: "string" },
|
|
11
|
+
"ca-key": { type: "string" },
|
|
12
|
+
subject: { type: "string" },
|
|
13
|
+
days: { type: "string", default: "1825" },
|
|
14
|
+
curve: { type: "string", default: "P-256" },
|
|
15
|
+
name: { type: "string", default: "intermediate" }
|
|
16
|
+
},
|
|
17
|
+
strict: true
|
|
18
|
+
});
|
|
19
|
+
const caCertPath = requireString(values["ca-cert"], "--ca-cert");
|
|
20
|
+
const caKeyPath = requireString(values["ca-key"], "--ca-key");
|
|
21
|
+
const subject = parseDnString(requireString(values.subject, "--subject"));
|
|
22
|
+
const days = parseDaysFlag(values.days);
|
|
23
|
+
const curve = parseCurveFlag(values.curve);
|
|
24
|
+
const basename = values.name;
|
|
25
|
+
const root = await loadCaFromDisk({ certPath: caCertPath, keyPath: caKeyPath });
|
|
26
|
+
const keyPair = await generateKeyPair(curve);
|
|
27
|
+
const intermediate = await issueIntermediateCA({ ca: root, subject, days, keyPair });
|
|
28
|
+
const written = await writeCaTriplet(intermediate, ".", basename);
|
|
29
|
+
console.log(`wrote ${written.certPath}`);
|
|
30
|
+
console.log(`wrote ${written.keyPath}`);
|
|
31
|
+
if (written.chainPath)
|
|
32
|
+
console.log(`wrote ${written.chainPath}`);
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pem-to-pfx.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/pem-to-pfx.ts"],"names":[],"mappings":"AAYA,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA+CnE"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { parseArgs } from "node:util";
|
|
3
|
+
import { exportPkcs12, importCertificateAuthority, pemToDer, pemToDerWithLabel, splitPemBlocks } from "../../index.js";
|
|
4
|
+
import { requireString } from "../flags.js";
|
|
5
|
+
import { defaultPfxPath, importPkcs8PrivateKeyFromPem } from "../io.js";
|
|
6
|
+
export async function pemToPfxCommand(args) {
|
|
7
|
+
const { values } = parseArgs({
|
|
8
|
+
args,
|
|
9
|
+
options: {
|
|
10
|
+
cert: { type: "string" },
|
|
11
|
+
key: { type: "string" },
|
|
12
|
+
chain: { type: "string" },
|
|
13
|
+
out: { type: "string" },
|
|
14
|
+
password: { type: "string" }
|
|
15
|
+
},
|
|
16
|
+
strict: true
|
|
17
|
+
});
|
|
18
|
+
const certPath = requireString(values.cert, "--cert");
|
|
19
|
+
const keyPath = requireString(values.key, "--key");
|
|
20
|
+
const password = requireString(values.password, "--password");
|
|
21
|
+
const chainPath = values.chain;
|
|
22
|
+
const outPath = values.out ?? defaultPfxPath(certPath);
|
|
23
|
+
const [certPem, keyPem, chainPem] = await Promise.all([
|
|
24
|
+
readFile(certPath, "utf8"),
|
|
25
|
+
readFile(keyPath, "utf8"),
|
|
26
|
+
chainPath ? readFile(chainPath, "utf8") : Promise.resolve("")
|
|
27
|
+
]);
|
|
28
|
+
const certDer = pemToDer(certPem);
|
|
29
|
+
const chainDer = chainPem
|
|
30
|
+
? splitPemBlocks(chainPem).map((block) => pemToDerWithLabel(block, "CERTIFICATE"))
|
|
31
|
+
: [];
|
|
32
|
+
const privateKey = await importPkcs8PrivateKeyFromPem(keyPem);
|
|
33
|
+
// Refuse to emit a PFX whose key does not match the leaf cert. The library's
|
|
34
|
+
// exportPkcs12 does not check this, so without the guard a mismatched pair
|
|
35
|
+
// silently produces a structurally valid but unusable .pfx file. We discard
|
|
36
|
+
// the returned CertificateAuthority — only the sign/verify round-trip
|
|
37
|
+
// performed inside importCertificateAuthority matters here.
|
|
38
|
+
await importCertificateAuthority({ certPem, privateKey });
|
|
39
|
+
const pfx = await exportPkcs12({
|
|
40
|
+
certDer,
|
|
41
|
+
...(chainDer.length > 0 ? { chainDer } : {}),
|
|
42
|
+
privateKey,
|
|
43
|
+
password: new TextEncoder().encode(password)
|
|
44
|
+
});
|
|
45
|
+
await writeFile(outPath, pfx);
|
|
46
|
+
console.log(`wrote ${outPath}`);
|
|
47
|
+
}
|
package/dist/cli/dn.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dn.d.ts","sourceRoot":"","sources":["../../src/cli/dn.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,OAAO,EAGR,MAAM,aAAa,CAAC;AAQrB,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAoBpD"}
|
package/dist/cli/dn.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { SUBJECT_ATTRIBUTE_OIDS } from "../oids.js";
|
|
2
|
+
const SHORT_NAMES = new Set(Object.keys(SUBJECT_ATTRIBUTE_OIDS));
|
|
3
|
+
const DOTTED_OID = /^\d+(?:\.\d+)+$/;
|
|
4
|
+
export function parseDnString(input) {
|
|
5
|
+
const parts = [];
|
|
6
|
+
for (const raw of splitTopLevel(input, ",")) {
|
|
7
|
+
const part = raw.trim();
|
|
8
|
+
if (part === "")
|
|
9
|
+
continue;
|
|
10
|
+
const eq = part.indexOf("=");
|
|
11
|
+
if (eq < 0) {
|
|
12
|
+
throw new Error(`Invalid DN component (missing '='): ${part}`);
|
|
13
|
+
}
|
|
14
|
+
const rawKey = part.slice(0, eq).trim();
|
|
15
|
+
const value = unescape(part.slice(eq + 1).trim());
|
|
16
|
+
if (rawKey === "")
|
|
17
|
+
throw new Error(`Invalid DN component (empty key): ${part}`);
|
|
18
|
+
if (value === "")
|
|
19
|
+
throw new Error(`Invalid DN component (empty value): ${part}`);
|
|
20
|
+
const type = resolveAttributeType(rawKey);
|
|
21
|
+
parts.push({ type, value });
|
|
22
|
+
}
|
|
23
|
+
if (parts.length === 0) {
|
|
24
|
+
throw new Error("Subject is empty");
|
|
25
|
+
}
|
|
26
|
+
return parts;
|
|
27
|
+
}
|
|
28
|
+
function resolveAttributeType(raw) {
|
|
29
|
+
const upper = raw.toUpperCase();
|
|
30
|
+
if (SHORT_NAMES.has(upper)) {
|
|
31
|
+
return upper;
|
|
32
|
+
}
|
|
33
|
+
if (DOTTED_OID.test(raw)) {
|
|
34
|
+
return raw;
|
|
35
|
+
}
|
|
36
|
+
throw new Error(`Unsupported DN attribute: ${raw}`);
|
|
37
|
+
}
|
|
38
|
+
function splitTopLevel(input, sep) {
|
|
39
|
+
const out = [];
|
|
40
|
+
let current = "";
|
|
41
|
+
let escaped = false;
|
|
42
|
+
for (const ch of input) {
|
|
43
|
+
if (escaped) {
|
|
44
|
+
current += ch;
|
|
45
|
+
escaped = false;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (ch === "\\") {
|
|
49
|
+
current += ch;
|
|
50
|
+
escaped = true;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (ch === sep) {
|
|
54
|
+
out.push(current);
|
|
55
|
+
current = "";
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
current += ch;
|
|
59
|
+
}
|
|
60
|
+
out.push(current);
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
function unescape(value) {
|
|
64
|
+
return value.replace(/\\(.)/g, "$1");
|
|
65
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { SupportedCurve } from "../index.js";
|
|
2
|
+
export declare class UsageError extends Error {
|
|
3
|
+
name: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function parseDaysFlag(value: string): number;
|
|
6
|
+
export declare function parseCurveFlag(value: string): SupportedCurve;
|
|
7
|
+
export declare function requireString(value: string | undefined, flagName: string): string;
|
|
8
|
+
//# sourceMappingURL=flags.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flags.d.ts","sourceRoot":"","sources":["../../src/cli/flags.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,qBAAa,UAAW,SAAQ,KAAK;IAC1B,IAAI,SAAgB;CAC9B;AASD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CASnD;AAID,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,CAK5D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKjF"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export class UsageError extends Error {
|
|
2
|
+
name = "UsageError";
|
|
3
|
+
}
|
|
4
|
+
// Accept only positive decimal integers with no leading zero, surrounding
|
|
5
|
+
// whitespace, sign, scientific notation, or fractional part. Number() is too
|
|
6
|
+
// permissive (it accepts " 365 ", "1e3", "0x10", "01", "1.0", and silently
|
|
7
|
+
// rounds beyond MAX_SAFE_INTEGER), which would let surprising values slip
|
|
8
|
+
// past CLI validation into the issuance API.
|
|
9
|
+
const POSITIVE_DECIMAL_INTEGER = /^[1-9]\d*$/;
|
|
10
|
+
export function parseDaysFlag(value) {
|
|
11
|
+
if (!POSITIVE_DECIMAL_INTEGER.test(value)) {
|
|
12
|
+
throw new UsageError(`--days must be a positive decimal integer, got: ${JSON.stringify(value)}`);
|
|
13
|
+
}
|
|
14
|
+
const n = Number(value);
|
|
15
|
+
if (!Number.isSafeInteger(n)) {
|
|
16
|
+
throw new UsageError(`--days exceeds the safe integer range: ${value}`);
|
|
17
|
+
}
|
|
18
|
+
return n;
|
|
19
|
+
}
|
|
20
|
+
const SUPPORTED_CURVES = ["P-256", "P-384", "P-521"];
|
|
21
|
+
export function parseCurveFlag(value) {
|
|
22
|
+
if (SUPPORTED_CURVES.includes(value)) {
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
throw new UsageError(`--curve must be one of P-256, P-384, P-521 (got: ${value})`);
|
|
26
|
+
}
|
|
27
|
+
export function requireString(value, flagName) {
|
|
28
|
+
if (value === undefined || value === "") {
|
|
29
|
+
throw new UsageError(`${flagName} is required`);
|
|
30
|
+
}
|
|
31
|
+
return value;
|
|
32
|
+
}
|
package/dist/cli/io.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type CertificateAuthority, type IssuedClientCertificate } from "../index.js";
|
|
2
|
+
export declare function importPkcs8PrivateKeyFromPem(pem: string): Promise<CryptoKey>;
|
|
3
|
+
export declare function cryptoKeyToPkcs8Pem(key: CryptoKey): Promise<string>;
|
|
4
|
+
export interface WriteCaResult {
|
|
5
|
+
certPath: string;
|
|
6
|
+
keyPath: string;
|
|
7
|
+
chainPath?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function writeCaTriplet(ca: CertificateAuthority, outDir: string, basename: string): Promise<WriteCaResult>;
|
|
10
|
+
export declare function writeIssuedLeafTriplet(issued: IssuedClientCertificate, outDir: string, basename: string): Promise<WriteCaResult>;
|
|
11
|
+
export declare function stripLeafFromChain(leafPem: string, chainPem: string): string;
|
|
12
|
+
export interface LoadCaInput {
|
|
13
|
+
certPath: string;
|
|
14
|
+
keyPath: string;
|
|
15
|
+
chainPath?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function loadCaFromDisk(input: LoadCaInput): Promise<CertificateAuthority>;
|
|
18
|
+
export declare function defaultPfxPath(certPath: string): string;
|
|
19
|
+
//# sourceMappingURL=io.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"io.d.ts","sourceRoot":"","sources":["../../src/cli/io.ts"],"names":[],"mappings":"AAEA,OAAO,EAQL,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC7B,MAAM,aAAa,CAAC;AAKrB,wBAAsB,4BAA4B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAmBlF;AAED,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAGzE;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,cAAc,CAClC,EAAE,EAAE,oBAAoB,EACxB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,aAAa,CAAC,CAoBxB;AAED,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,uBAAuB,EAC/B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,aAAa,CAAC,CAoBxB;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQ5E;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAWtF;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAOvD"}
|
package/dist/cli/io.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { arrayBufferFromBytes, bytesEqual, encodePem, importCertificateAuthority, pemToDer, pemToDerWithLabel, splitPemBlocks } from "../index.js";
|
|
4
|
+
const EC_CURVES = ["P-256", "P-384", "P-521"];
|
|
5
|
+
export async function importPkcs8PrivateKeyFromPem(pem) {
|
|
6
|
+
const der = pemToDerWithLabel(pem, "PRIVATE KEY");
|
|
7
|
+
let lastError;
|
|
8
|
+
for (const namedCurve of EC_CURVES) {
|
|
9
|
+
try {
|
|
10
|
+
return await crypto.subtle.importKey("pkcs8", arrayBufferFromBytes(der), { name: "ECDSA", namedCurve }, true, ["sign"]);
|
|
11
|
+
}
|
|
12
|
+
catch (err) {
|
|
13
|
+
lastError = err;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
throw new Error(`Unable to import PRIVATE KEY as ECDSA P-256/P-384/P-521 PKCS#8: ${describe(lastError)}`);
|
|
17
|
+
}
|
|
18
|
+
export async function cryptoKeyToPkcs8Pem(key) {
|
|
19
|
+
const der = new Uint8Array(await crypto.subtle.exportKey("pkcs8", key));
|
|
20
|
+
return encodePem("PRIVATE KEY", der);
|
|
21
|
+
}
|
|
22
|
+
export async function writeCaTriplet(ca, outDir, basename) {
|
|
23
|
+
const certPath = path.join(outDir, `${basename}.crt.pem`);
|
|
24
|
+
const keyPath = path.join(outDir, `${basename}.key.pem`);
|
|
25
|
+
const keyPem = await cryptoKeyToPkcs8Pem(ca.privateKey);
|
|
26
|
+
const writes = [
|
|
27
|
+
writeFile(certPath, ca.certPem, "utf8"),
|
|
28
|
+
writeFile(keyPath, keyPem, "utf8")
|
|
29
|
+
];
|
|
30
|
+
const hasChain = ca.issuerChainPem.trim().length > 0;
|
|
31
|
+
const chainPath = hasChain ? path.join(outDir, `${basename}.chain.pem`) : undefined;
|
|
32
|
+
if (hasChain && chainPath) {
|
|
33
|
+
writes.push(writeFile(chainPath, ca.issuerChainPem, "utf8"));
|
|
34
|
+
}
|
|
35
|
+
await Promise.all(writes);
|
|
36
|
+
return chainPath !== undefined
|
|
37
|
+
? { certPath, keyPath, chainPath }
|
|
38
|
+
: { certPath, keyPath };
|
|
39
|
+
}
|
|
40
|
+
export async function writeIssuedLeafTriplet(issued, outDir, basename) {
|
|
41
|
+
const certPath = path.join(outDir, `${basename}.crt.pem`);
|
|
42
|
+
const keyPath = path.join(outDir, `${basename}.key.pem`);
|
|
43
|
+
const chainPath = path.join(outDir, `${basename}.chain.pem`);
|
|
44
|
+
const keyPem = await cryptoKeyToPkcs8Pem(issued.privateKey);
|
|
45
|
+
// The library's `certChainPem` is a fullchain (leaf + issuers) suitable for
|
|
46
|
+
// nginx-style server config. The CLI convention for `*.chain.pem` is the
|
|
47
|
+
// issuer-only chain (no leaf), matching what `pem-to-pfx --chain` expects.
|
|
48
|
+
// Strip the leaf out by DER match so the file faithfully represents an
|
|
49
|
+
// issuer-only chain regardless of where the leaf sits in certChainPem.
|
|
50
|
+
const issuerOnlyChainPem = stripLeafFromChain(issued.certPem, issued.certChainPem);
|
|
51
|
+
await Promise.all([
|
|
52
|
+
writeFile(certPath, issued.certPem, "utf8"),
|
|
53
|
+
writeFile(keyPath, keyPem, "utf8"),
|
|
54
|
+
writeFile(chainPath, issuerOnlyChainPem, "utf8")
|
|
55
|
+
]);
|
|
56
|
+
return { certPath, keyPath, chainPath };
|
|
57
|
+
}
|
|
58
|
+
export function stripLeafFromChain(leafPem, chainPem) {
|
|
59
|
+
const leafDer = pemToDer(leafPem);
|
|
60
|
+
const issuerBlocks = splitPemBlocks(chainPem).filter((block) => {
|
|
61
|
+
const blockDer = pemToDerWithLabel(block, "CERTIFICATE");
|
|
62
|
+
return !bytesEqual(blockDer, leafDer);
|
|
63
|
+
});
|
|
64
|
+
if (issuerBlocks.length === 0)
|
|
65
|
+
return "";
|
|
66
|
+
return issuerBlocks.join("\n") + "\n";
|
|
67
|
+
}
|
|
68
|
+
export async function loadCaFromDisk(input) {
|
|
69
|
+
const [certPem, keyPem, chainPem] = await Promise.all([
|
|
70
|
+
readFile(input.certPath, "utf8"),
|
|
71
|
+
readFile(input.keyPath, "utf8"),
|
|
72
|
+
input.chainPath ? readFile(input.chainPath, "utf8") : Promise.resolve("")
|
|
73
|
+
]);
|
|
74
|
+
return importCertificateAuthority({
|
|
75
|
+
certPem,
|
|
76
|
+
privateKey: await importPkcs8PrivateKeyFromPem(keyPem),
|
|
77
|
+
issuerChainPem: chainPem
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
export function defaultPfxPath(certPath) {
|
|
81
|
+
const dir = path.dirname(certPath);
|
|
82
|
+
const base = path
|
|
83
|
+
.basename(certPath)
|
|
84
|
+
.replace(/\.crt\.pem$/i, "")
|
|
85
|
+
.replace(/\.pem$/i, "");
|
|
86
|
+
return path.join(dir, `${base}.pfx`);
|
|
87
|
+
}
|
|
88
|
+
function describe(err) {
|
|
89
|
+
if (err instanceof Error)
|
|
90
|
+
return err.message;
|
|
91
|
+
return String(err);
|
|
92
|
+
}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRootCaCommand } from "./cli/commands/create-root-ca.js";
|
|
3
|
+
import { issueClientCommand } from "./cli/commands/issue-client.js";
|
|
4
|
+
import { issueIntermediateCaCommand } from "./cli/commands/issue-intermediate-ca.js";
|
|
5
|
+
import { pemToPfxCommand } from "./cli/commands/pem-to-pfx.js";
|
|
6
|
+
import { UsageError } from "./cli/flags.js";
|
|
7
|
+
const USAGE = `Usage:
|
|
8
|
+
edgca create-root-ca --subject <dn> [--days 3650] [--curve P-256] [--name root]
|
|
9
|
+
|
|
10
|
+
edgca issue-intermediate-ca --ca-cert <pem> --ca-key <pem>
|
|
11
|
+
--subject <dn>
|
|
12
|
+
[--days 1825] [--curve P-256] [--name intermediate]
|
|
13
|
+
|
|
14
|
+
edgca issue-client --ca-cert <pem> --ca-key <pem> [--ca-chain <pem>]
|
|
15
|
+
--subject <dn>
|
|
16
|
+
[--dns-name <name>]... [--ip <addr>]...
|
|
17
|
+
[--days 365] [--name client]
|
|
18
|
+
|
|
19
|
+
edgca pem-to-pfx --cert <pem> --key <pem> --password <pw>
|
|
20
|
+
[--chain <pem>] [--out <pfx>]
|
|
21
|
+
`;
|
|
22
|
+
async function main() {
|
|
23
|
+
const [sub, ...rest] = process.argv.slice(2);
|
|
24
|
+
switch (sub) {
|
|
25
|
+
case "create-root-ca":
|
|
26
|
+
await createRootCaCommand(rest);
|
|
27
|
+
return;
|
|
28
|
+
case "issue-intermediate-ca":
|
|
29
|
+
await issueIntermediateCaCommand(rest);
|
|
30
|
+
return;
|
|
31
|
+
case "issue-client":
|
|
32
|
+
await issueClientCommand(rest);
|
|
33
|
+
return;
|
|
34
|
+
case "pem-to-pfx":
|
|
35
|
+
await pemToPfxCommand(rest);
|
|
36
|
+
return;
|
|
37
|
+
case undefined:
|
|
38
|
+
case "-h":
|
|
39
|
+
case "--help":
|
|
40
|
+
process.stdout.write(USAGE);
|
|
41
|
+
return;
|
|
42
|
+
default:
|
|
43
|
+
process.stderr.write(`unknown subcommand: ${sub}\n\n${USAGE}`);
|
|
44
|
+
process.exitCode = 2;
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
main().catch((err) => {
|
|
49
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
50
|
+
process.stderr.write(`${message}\n`);
|
|
51
|
+
process.exitCode = err instanceof UsageError ? 2 : 1;
|
|
52
|
+
});
|
package/dist/csr.d.ts
CHANGED
|
@@ -22,4 +22,16 @@ export interface ParsedCertificateSigningRequest {
|
|
|
22
22
|
}
|
|
23
23
|
export declare function parseCertificateSigningRequest(input: string | Uint8Array): Promise<ParsedCertificateSigningRequest>;
|
|
24
24
|
export declare function verifyCertificateSigningRequestSignature(csr: ParsedCertificateSigningRequest): Promise<boolean>;
|
|
25
|
+
export interface CreateCertificateSigningRequestInput {
|
|
26
|
+
subject: Subject;
|
|
27
|
+
keyPair: CryptoKeyPair;
|
|
28
|
+
dnsNames?: readonly string[];
|
|
29
|
+
ipAddresses?: readonly string[];
|
|
30
|
+
extensions?: readonly CertificateSigningRequestExtension[];
|
|
31
|
+
}
|
|
32
|
+
export interface CreatedCertificateSigningRequest {
|
|
33
|
+
der: Uint8Array;
|
|
34
|
+
pem: string;
|
|
35
|
+
}
|
|
36
|
+
export declare function createCertificateSigningRequest(input: CreateCertificateSigningRequestInput): Promise<CreatedCertificateSigningRequest>;
|
|
25
37
|
//# sourceMappingURL=csr.d.ts.map
|
package/dist/csr.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"csr.d.ts","sourceRoot":"","sources":["../src/csr.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"csr.d.ts","sourceRoot":"","sources":["../src/csr.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,OAAO,EAAwB,MAAM,YAAY,CAAC;AAmBhE,MAAM,WAAW,kCAAkC;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,UAAU,CAAC;CACtB;AAED,MAAM,WAAW,kCAAkC;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,+BAA+B;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,SAAS,CAAC;IACrB,uBAAuB,EAAE,UAAU,CAAC;IACpC,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,oBAAoB,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,mBAAmB,EAAE,SAAS,kCAAkC,EAAE,CAAC;IACnE,eAAe,EAAE,SAAS,kCAAkC,EAAE,CAAC;IAC/D,qBAAqB,EAAE,MAAM,CAAC;IAC9B,YAAY,EAAE,UAAU,CAAC;IACzB,2BAA2B,EAAE,UAAU,CAAC;CACzC;AAED,wBAAsB,8BAA8B,CAClD,KAAK,EAAE,MAAM,GAAG,UAAU,GACzB,OAAO,CAAC,+BAA+B,CAAC,CAwE1C;AAED,wBAAsB,wCAAwC,CAC5D,GAAG,EAAE,+BAA+B,GACnC,OAAO,CAAC,OAAO,CAAC,CAElB;AAED,MAAM,WAAW,oCAAoC;IACnD,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,aAAa,CAAC;IACvB,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,UAAU,CAAC,EAAE,SAAS,kCAAkC,EAAE,CAAC;CAC5D;AAED,MAAM,WAAW,gCAAgC;IAC/C,GAAG,EAAE,UAAU,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAsB,+BAA+B,CACnD,KAAK,EAAE,oCAAoC,GAC1C,OAAO,CAAC,gCAAgC,CAAC,CAiD3C"}
|
package/dist/csr.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { cloneBytes } from "./bytes.js";
|
|
2
|
-
import { importPublicKeySpki, verifyDer } from "./crypto.js";
|
|
3
|
-
import { decodeInteger, decodeOid, readChildren, readElement, readSequenceChildren, TAG } from "./der.js";
|
|
1
|
+
import { cloneBytes, concatBytes } from "./bytes.js";
|
|
2
|
+
import { curveOf, exportSpki, importPublicKeySpki, signDer, signatureAlgorithmOidForCurve, verifyDer } from "./crypto.js";
|
|
3
|
+
import { bitString, decodeInteger, decodeOid, der, integer, oid, readChildren, readElement, readSequenceChildren, sequence, set, TAG } from "./der.js";
|
|
4
|
+
import { encodeName } from "./name.js";
|
|
4
5
|
import { OID, SUBJECT_ATTRIBUTE_OIDS } from "./oids.js";
|
|
5
|
-
import { pemToDerWithLabel, splitPemBlocks } from "./pem.js";
|
|
6
|
+
import { csrToPem, pemToDerWithLabel, splitPemBlocks } from "./pem.js";
|
|
7
|
+
import { extension, subjectAltNameExtension } from "./x509.js";
|
|
6
8
|
const SHORT_NAME_BY_OID = Object.fromEntries(Object.entries(SUBJECT_ATTRIBUTE_OIDS).map(([shortName, oid]) => [oid, shortName]));
|
|
7
9
|
const VALUE_DECODERS = new Map([
|
|
8
10
|
[TAG.UTF8_STRING, (b) => new TextDecoder("utf-8", { fatal: true }).decode(b)],
|
|
@@ -15,9 +17,9 @@ const SUPPORTED_SIGNATURE_OIDS = new Set([
|
|
|
15
17
|
OID.ecdsaWithSha512
|
|
16
18
|
]);
|
|
17
19
|
export async function parseCertificateSigningRequest(input) {
|
|
18
|
-
const
|
|
19
|
-
const root = readElement(
|
|
20
|
-
if (root.tag !== TAG.SEQUENCE || root.end !==
|
|
20
|
+
const derBytes = typeof input === "string" ? csrPemToDer(input) : input;
|
|
21
|
+
const root = readElement(derBytes);
|
|
22
|
+
if (root.tag !== TAG.SEQUENCE || root.end !== derBytes.length) {
|
|
21
23
|
throw new Error("Invalid CSR DER");
|
|
22
24
|
}
|
|
23
25
|
const [requestInfo, signatureAlgorithm, signatureValue] = readSequenceChildren(root);
|
|
@@ -62,7 +64,7 @@ export async function parseCertificateSigningRequest(input) {
|
|
|
62
64
|
const requestedExtensions = extensionRequest
|
|
63
65
|
? decodeRequestedExtensions(extensionRequest.valuesDer)
|
|
64
66
|
: [];
|
|
65
|
-
const sanExtension = requestedExtensions.find((
|
|
67
|
+
const sanExtension = requestedExtensions.find((ext) => ext.oid === OID.subjectAltName);
|
|
66
68
|
const { dnsNames, ipAddresses } = sanExtension
|
|
67
69
|
? decodeSubjectAltName(sanExtension.valueDer)
|
|
68
70
|
: { dnsNames: [], ipAddresses: [] };
|
|
@@ -82,6 +84,48 @@ export async function parseCertificateSigningRequest(input) {
|
|
|
82
84
|
export async function verifyCertificateSigningRequestSignature(csr) {
|
|
83
85
|
return verifyDer(csr.publicKey, csr.signatureDer, csr.certificationRequestInfoDer);
|
|
84
86
|
}
|
|
87
|
+
export async function createCertificateSigningRequest(input) {
|
|
88
|
+
const privateCurve = curveOf(input.keyPair.privateKey);
|
|
89
|
+
const publicCurve = curveOf(input.keyPair.publicKey);
|
|
90
|
+
if (privateCurve !== publicCurve) {
|
|
91
|
+
throw new Error("CSR keyPair private/public curve mismatch");
|
|
92
|
+
}
|
|
93
|
+
const subjectNameDer = encodeName(input.subject);
|
|
94
|
+
const spki = await exportSpki(input.keyPair.publicKey);
|
|
95
|
+
const requestedExtensions = collectRequestedExtensions(input.dnsNames, input.ipAddresses, input.extensions);
|
|
96
|
+
const attributes = [];
|
|
97
|
+
if (requestedExtensions.length > 0) {
|
|
98
|
+
attributes.push(sequence(oid(OID.extensionRequest), set(sequence(...requestedExtensions))));
|
|
99
|
+
}
|
|
100
|
+
// CertificationRequestInfo.attributes is IMPLICIT [0] SET OF Attribute.
|
|
101
|
+
// Wire form: tag 0xa0 wrapping the concatenated Attribute SEQUENCEs.
|
|
102
|
+
const attributesField = der(0xa0, concatBytes(attributes));
|
|
103
|
+
const certificationRequestInfoDer = sequence(integer(0), subjectNameDer, spki, attributesField);
|
|
104
|
+
const signatureDer = await signDer(input.keyPair.privateKey, certificationRequestInfoDer);
|
|
105
|
+
const signatureAlgorithmOid = signatureAlgorithmOidForCurve(privateCurve);
|
|
106
|
+
const csrDer = sequence(certificationRequestInfoDer, sequence(oid(signatureAlgorithmOid)), bitString(signatureDer));
|
|
107
|
+
return {
|
|
108
|
+
der: csrDer,
|
|
109
|
+
pem: csrToPem(csrDer)
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function collectRequestedExtensions(dnsNames, ipAddresses, extraExtensions) {
|
|
113
|
+
const seenOids = new Set();
|
|
114
|
+
const out = [];
|
|
115
|
+
const san = subjectAltNameExtension(dnsNames, ipAddresses);
|
|
116
|
+
if (san) {
|
|
117
|
+
seenOids.add(OID.subjectAltName);
|
|
118
|
+
out.push(san);
|
|
119
|
+
}
|
|
120
|
+
for (const ext of extraExtensions ?? []) {
|
|
121
|
+
if (seenOids.has(ext.oid)) {
|
|
122
|
+
throw new Error(`Duplicate CSR extension OID: ${ext.oid}`);
|
|
123
|
+
}
|
|
124
|
+
seenOids.add(ext.oid);
|
|
125
|
+
out.push(extension(ext.oid, ext.critical, ext.valueDer));
|
|
126
|
+
}
|
|
127
|
+
return out;
|
|
128
|
+
}
|
|
85
129
|
function csrPemToDer(pem) {
|
|
86
130
|
// RFC 7468 §7 uses "CERTIFICATE REQUEST"; some legacy tools emit "NEW CERTIFICATE REQUEST".
|
|
87
131
|
const blocks = splitPemBlocks(pem);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
export { createRootCA, importCertificateAuthority, issueClientCert, issueClientCertForPublicKey, issueIntermediateCA } from "./ca.js";
|
|
2
|
-
export { parseCertificateSigningRequest, verifyCertificateSigningRequestSignature, type
|
|
3
|
-
export { pemToDer,
|
|
1
|
+
export { createRootCA, importCertificateAuthority, issueClientCert, issueClientCertForPublicKey, issueDocumentSigningCert, issueIntermediateCA } from "./ca.js";
|
|
2
|
+
export { createCertificateSigningRequest, parseCertificateSigningRequest, verifyCertificateSigningRequestSignature, type CertificateSigningRequestAttribute, type CertificateSigningRequestExtension, type CreateCertificateSigningRequestInput, type CreatedCertificateSigningRequest, type ParsedCertificateSigningRequest } from "./csr.js";
|
|
3
|
+
export { certificateToPem, csrToPem, encodePem, pemToDer, pemToDerWithLabel, splitPemBlocks } from "./pem.js";
|
|
4
|
+
export { generateKeyPair, type SupportedCurve } from "./crypto.js";
|
|
5
|
+
export { arrayBufferFromBytes, bytesEqual } from "./bytes.js";
|
|
4
6
|
export { exportPkcs12, type ExportPkcs12Input } from "./pkcs12.js";
|
|
5
7
|
export { verifyClientCertificateIssuedBy, type VerifyClientCertificateIssuedByOptions, type VerifyClientCertificateValidity } from "./verify.js";
|
|
6
|
-
export type { CertificateAuthority, CreateRootCAOptions, ImportCertificateAuthorityOptions, IssueClientCertForPublicKeyOptions, IssueClientCertOptions, IssueIntermediateCAOptions, IssuedClientCertificate, IssuedClientCertificateForPublicKey, SerialNumber, ShortSubjectAttributeType, Subject, SubjectAttribute, SubjectAttributeType } from "./types.js";
|
|
8
|
+
export type { CertificateAuthority, CreateRootCAOptions, ImportCertificateAuthorityOptions, IssueClientCertForPublicKeyOptions, IssueClientCertOptions, IssueDocumentSigningCertOptions, IssueIntermediateCAOptions, IssuedClientCertificate, IssuedClientCertificateForPublicKey, IssuedDocumentSigningCertificate, SerialNumber, ShortSubjectAttributeType, Subject, SubjectAttribute, SubjectAttributeType } from "./types.js";
|
|
7
9
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,0BAA0B,EAC1B,eAAe,EACf,2BAA2B,EAC3B,mBAAmB,EACpB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,8BAA8B,EAC9B,wCAAwC,EACxC,KAAK
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,0BAA0B,EAC1B,eAAe,EACf,2BAA2B,EAC3B,wBAAwB,EACxB,mBAAmB,EACpB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,+BAA+B,EAC/B,8BAA8B,EAC9B,wCAAwC,EACxC,KAAK,kCAAkC,EACvC,KAAK,kCAAkC,EACvC,KAAK,oCAAoC,EACzC,KAAK,gCAAgC,EACrC,KAAK,+BAA+B,EACrC,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,gBAAgB,EAChB,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,iBAAiB,EACjB,cAAc,EACf,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EACL,+BAA+B,EAC/B,KAAK,sCAAsC,EAC3C,KAAK,+BAA+B,EACrC,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,oBAAoB,EACpB,mBAAmB,EACnB,iCAAiC,EACjC,kCAAkC,EAClC,sBAAsB,EACtB,+BAA+B,EAC/B,0BAA0B,EAC1B,uBAAuB,EACvB,mCAAmC,EACnC,gCAAgC,EAChC,YAAY,EACZ,yBAAyB,EACzB,OAAO,EACP,gBAAgB,EAChB,oBAAoB,EACrB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
export { createRootCA, importCertificateAuthority, issueClientCert, issueClientCertForPublicKey, issueIntermediateCA } from "./ca.js";
|
|
2
|
-
export { parseCertificateSigningRequest, verifyCertificateSigningRequestSignature } from "./csr.js";
|
|
3
|
-
export { pemToDer,
|
|
1
|
+
export { createRootCA, importCertificateAuthority, issueClientCert, issueClientCertForPublicKey, issueDocumentSigningCert, issueIntermediateCA } from "./ca.js";
|
|
2
|
+
export { createCertificateSigningRequest, parseCertificateSigningRequest, verifyCertificateSigningRequestSignature } from "./csr.js";
|
|
3
|
+
export { certificateToPem, csrToPem, encodePem, pemToDer, pemToDerWithLabel, splitPemBlocks } from "./pem.js";
|
|
4
|
+
export { generateKeyPair } from "./crypto.js";
|
|
5
|
+
export { arrayBufferFromBytes, bytesEqual } from "./bytes.js";
|
|
4
6
|
export { exportPkcs12 } from "./pkcs12.js";
|
|
5
7
|
export { verifyClientCertificateIssuedBy } from "./verify.js";
|
package/dist/oids.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export declare const OID: {
|
|
|
14
14
|
readonly subjectKeyIdentifier: "2.5.29.14";
|
|
15
15
|
readonly authorityKeyIdentifier: "2.5.29.35";
|
|
16
16
|
readonly clientAuth: "1.3.6.1.5.5.7.3.2";
|
|
17
|
+
readonly documentSigning: "1.3.6.1.5.5.7.3.36";
|
|
17
18
|
readonly extensionRequest: "1.2.840.113549.1.9.14";
|
|
18
19
|
readonly data: "1.2.840.113549.1.7.1";
|
|
19
20
|
readonly encryptedData: "1.2.840.113549.1.7.6";
|
package/dist/oids.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oids.d.ts","sourceRoot":"","sources":["../src/oids.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAE5D,eAAO,MAAM,GAAG
|
|
1
|
+
{"version":3,"file":"oids.d.ts","sourceRoot":"","sources":["../src/oids.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAE5D,eAAO,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BN,CAAC;AAEX,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,yBAAyB,EAAE,MAAM,CAgB5E,CAAC;AAGF,eAAO,MAAM,2BAA2B,EAAE,MAAM,CAAC,yBAAyB,EAAE,MAAM,CAgBjF,CAAC;AAIF,eAAO,MAAM,wBAAwB,MAAM,CAAC"}
|
package/dist/oids.js
CHANGED
|
@@ -13,6 +13,7 @@ export const OID = {
|
|
|
13
13
|
subjectKeyIdentifier: "2.5.29.14",
|
|
14
14
|
authorityKeyIdentifier: "2.5.29.35",
|
|
15
15
|
clientAuth: "1.3.6.1.5.5.7.3.2",
|
|
16
|
+
documentSigning: "1.3.6.1.5.5.7.3.36",
|
|
16
17
|
extensionRequest: "1.2.840.113549.1.9.14",
|
|
17
18
|
data: "1.2.840.113549.1.7.1",
|
|
18
19
|
encryptedData: "1.2.840.113549.1.7.6",
|
package/dist/pem.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export declare function certificateToPem(der: Uint8Array): string;
|
|
2
|
+
export declare function csrToPem(der: Uint8Array): string;
|
|
2
3
|
export declare function pemToDer(pem: string): Uint8Array;
|
|
3
4
|
export declare function pemToDerWithLabel(pem: string, label: string): Uint8Array;
|
|
4
5
|
export declare function splitPemBlocks(pem: string): string[];
|
|
6
|
+
export declare function encodePem(label: string, der: Uint8Array): string;
|
|
5
7
|
//# sourceMappingURL=pem.d.ts.map
|
package/dist/pem.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pem.d.ts","sourceRoot":"","sources":["../src/pem.ts"],"names":[],"mappings":"AAIA,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAExD;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAWhD;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,CAaxE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAUpD"}
|
|
1
|
+
{"version":3,"file":"pem.d.ts","sourceRoot":"","sources":["../src/pem.ts"],"names":[],"mappings":"AAIA,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAExD;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAEhD;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAWhD;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,CAaxE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAUpD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,MAAM,CAShE"}
|
package/dist/pem.js
CHANGED
|
@@ -3,6 +3,9 @@ const PEM_LINE_LENGTH = 64;
|
|
|
3
3
|
export function certificateToPem(der) {
|
|
4
4
|
return encodePem("CERTIFICATE", der);
|
|
5
5
|
}
|
|
6
|
+
export function csrToPem(der) {
|
|
7
|
+
return encodePem("CERTIFICATE REQUEST", der);
|
|
8
|
+
}
|
|
6
9
|
export function pemToDer(pem) {
|
|
7
10
|
const match = /-----BEGIN (.+?)-----([\s\S]*?)-----END \1-----/.exec(pem);
|
|
8
11
|
if (!match || !match[2]) {
|
|
@@ -36,7 +39,7 @@ export function splitPemBlocks(pem) {
|
|
|
36
39
|
}
|
|
37
40
|
return blocks;
|
|
38
41
|
}
|
|
39
|
-
function encodePem(label, der) {
|
|
42
|
+
export function encodePem(label, der) {
|
|
40
43
|
const base64 = btoa(bytesToBinary(der));
|
|
41
44
|
const lines = [];
|
|
42
45
|
for (let i = 0; i < base64.length; i += PEM_LINE_LENGTH) {
|
package/dist/types.d.ts
CHANGED
|
@@ -62,6 +62,20 @@ export interface IssuedClientCertificateForPublicKey {
|
|
|
62
62
|
certDer: Uint8Array;
|
|
63
63
|
certChainPem: string;
|
|
64
64
|
}
|
|
65
|
+
export interface IssueDocumentSigningCertOptions {
|
|
66
|
+
ca: CertificateAuthority;
|
|
67
|
+
subject: Subject;
|
|
68
|
+
days: number;
|
|
69
|
+
notBefore?: Date;
|
|
70
|
+
serialNumber?: SerialNumber;
|
|
71
|
+
}
|
|
72
|
+
export interface IssuedDocumentSigningCertificate {
|
|
73
|
+
certPem: string;
|
|
74
|
+
certDer: Uint8Array;
|
|
75
|
+
privateKey: CryptoKey;
|
|
76
|
+
publicKey: CryptoKey;
|
|
77
|
+
certChainPem: string;
|
|
78
|
+
}
|
|
65
79
|
export interface ImportCertificateAuthorityOptions {
|
|
66
80
|
certPem: string;
|
|
67
81
|
privateKey: CryptoKey;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,yBAAyB,GACjC,IAAI,GACJ,GAAG,GACH,IAAI,GACJ,GAAG,GACH,IAAI,GACJ,GAAG,GACH,GAAG,GACH,IAAI,GACJ,cAAc,GACd,QAAQ,GACR,YAAY,GACZ,OAAO,GACP,WAAW,GACX,SAAS,GACT,KAAK,CAAC;AAEV,MAAM,MAAM,SAAS,GAAG,GAAG,MAAM,IAAI,MAAM,GAAG,MAAM,EAAE,CAAC;AAEvD,MAAM,MAAM,oBAAoB,GAAG,yBAAyB,GAAG,SAAS,CAAC;AAEzE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,oBAAoB,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;AAEzC,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;AAEjE,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,UAAU,CAAC;IACpB,UAAU,EAAE,SAAS,CAAC;IACtB,SAAS,EAAE,SAAS,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,UAAU,CAAC;IACpB,UAAU,EAAE,SAAS,CAAC;IACtB,SAAS,EAAE,SAAS,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,oBAAoB,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,oBAAoB,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,kCAAkC;IACjD,EAAE,EAAE,oBAAoB,CAAC;IACzB,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,mCAAmC;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,UAAU,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iCAAiC;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,SAAS,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,yBAAyB,GACjC,IAAI,GACJ,GAAG,GACH,IAAI,GACJ,GAAG,GACH,IAAI,GACJ,GAAG,GACH,GAAG,GACH,IAAI,GACJ,cAAc,GACd,QAAQ,GACR,YAAY,GACZ,OAAO,GACP,WAAW,GACX,SAAS,GACT,KAAK,CAAC;AAEV,MAAM,MAAM,SAAS,GAAG,GAAG,MAAM,IAAI,MAAM,GAAG,MAAM,EAAE,CAAC;AAEvD,MAAM,MAAM,oBAAoB,GAAG,yBAAyB,GAAG,SAAS,CAAC;AAEzE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,oBAAoB,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;AAEzC,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;AAEjE,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,UAAU,CAAC;IACpB,UAAU,EAAE,SAAS,CAAC;IACtB,SAAS,EAAE,SAAS,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,UAAU,CAAC;IACpB,UAAU,EAAE,SAAS,CAAC;IACtB,SAAS,EAAE,SAAS,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,oBAAoB,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,oBAAoB,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,kCAAkC;IACjD,EAAE,EAAE,oBAAoB,CAAC;IACzB,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,mCAAmC;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,UAAU,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,+BAA+B;IAC9C,EAAE,EAAE,oBAAoB,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,MAAM,WAAW,gCAAgC;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,UAAU,CAAC;IACpB,UAAU,EAAE,SAAS,CAAC;IACtB,SAAS,EAAE,SAAS,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iCAAiC;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,SAAS,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB"}
|
package/dist/x509.d.ts
CHANGED
|
@@ -23,11 +23,14 @@ export declare function basicConstraintsCaExtension(pathLenConstraint: number):
|
|
|
23
23
|
export declare function basicConstraintsLeafExtension(): Uint8Array;
|
|
24
24
|
export declare function keyUsageExtension(usages: readonly KeyUsageBit[]): Uint8Array;
|
|
25
25
|
export declare function extendedKeyUsageClientAuthExtension(): Uint8Array;
|
|
26
|
+
export declare function extendedKeyUsageDocumentSigningExtension(): Uint8Array;
|
|
26
27
|
export declare function subjectKeyIdentifierExtension(keyIdentifier: Uint8Array): Uint8Array;
|
|
27
28
|
export declare function authorityKeyIdentifierExtension(keyIdentifier: Uint8Array): Uint8Array;
|
|
28
29
|
export declare function subjectAltNameExtension(dnsNames?: readonly string[], ipAddresses?: readonly string[]): Uint8Array | undefined;
|
|
30
|
+
export declare function extension(extensionOid: string, critical: boolean, valueDer: Uint8Array): Uint8Array;
|
|
29
31
|
declare const KEY_USAGE_BITS: {
|
|
30
32
|
readonly digitalSignature: 0;
|
|
33
|
+
readonly contentCommitment: 1;
|
|
31
34
|
readonly keyCertSign: 5;
|
|
32
35
|
readonly cRLSign: 6;
|
|
33
36
|
};
|
package/dist/x509.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"x509.d.ts","sourceRoot":"","sources":["../src/x509.ts"],"names":[],"mappings":"AACA,OAAO,EAAiC,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAkBjF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,WAAW,qBAAqB;IACpC,YAAY,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IACxC,SAAS,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,UAAU,CAAC;IAC1B,cAAc,EAAE,UAAU,CAAC;IAC3B,uBAAuB,EAAE,UAAU,CAAC;IACpC,UAAU,EAAE,UAAU,EAAE,CAAC;IACzB,WAAW,EAAE,cAAc,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACnC,iBAAiB,EAAE,UAAU,CAAC;IAC9B,eAAe,EAAE,UAAU,CAAC;IAC5B,SAAS,EAAE,IAAI,CAAC;IAChB,QAAQ,EAAE,IAAI,CAAC;CAChB;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,qBAAqB,GAAG,oBAAoB,CAoBtF;AAED,wBAAgB,gBAAgB,CAC9B,iBAAiB,EAAE,UAAU,EAC7B,YAAY,EAAE,UAAU,EACxB,WAAW,EAAE,cAAc,GAC1B,UAAU,CAEZ;AAED,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,cAAc,GAAG,UAAU,CAEnF;AAED,wBAAgB,2BAA2B,CAAC,iBAAiB,EAAE,MAAM,GAAG,UAAU,CAMjF;AAED,wBAAgB,6BAA6B,IAAI,UAAU,CAE1D;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,UAAU,CAgB5E;AAED,wBAAgB,mCAAmC,IAAI,UAAU,CAEhE;AAED,wBAAgB,6BAA6B,CAAC,aAAa,EAAE,UAAU,GAAG,UAAU,CAEnF;AAED,wBAAgB,+BAA+B,CAAC,aAAa,EAAE,UAAU,GAAG,UAAU,CAErF;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,EAAE,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,GAAG,SAAS,CAmC7H;
|
|
1
|
+
{"version":3,"file":"x509.d.ts","sourceRoot":"","sources":["../src/x509.ts"],"names":[],"mappings":"AACA,OAAO,EAAiC,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAkBjF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,WAAW,qBAAqB;IACpC,YAAY,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IACxC,SAAS,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,UAAU,CAAC;IAC1B,cAAc,EAAE,UAAU,CAAC;IAC3B,uBAAuB,EAAE,UAAU,CAAC;IACpC,UAAU,EAAE,UAAU,EAAE,CAAC;IACzB,WAAW,EAAE,cAAc,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACnC,iBAAiB,EAAE,UAAU,CAAC;IAC9B,eAAe,EAAE,UAAU,CAAC;IAC5B,SAAS,EAAE,IAAI,CAAC;IAChB,QAAQ,EAAE,IAAI,CAAC;CAChB;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,qBAAqB,GAAG,oBAAoB,CAoBtF;AAED,wBAAgB,gBAAgB,CAC9B,iBAAiB,EAAE,UAAU,EAC7B,YAAY,EAAE,UAAU,EACxB,WAAW,EAAE,cAAc,GAC1B,UAAU,CAEZ;AAED,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,cAAc,GAAG,UAAU,CAEnF;AAED,wBAAgB,2BAA2B,CAAC,iBAAiB,EAAE,MAAM,GAAG,UAAU,CAMjF;AAED,wBAAgB,6BAA6B,IAAI,UAAU,CAE1D;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,UAAU,CAgB5E;AAED,wBAAgB,mCAAmC,IAAI,UAAU,CAEhE;AAED,wBAAgB,wCAAwC,IAAI,UAAU,CAErE;AAED,wBAAgB,6BAA6B,CAAC,aAAa,EAAE,UAAU,GAAG,UAAU,CAEnF;AAED,wBAAgB,+BAA+B,CAAC,aAAa,EAAE,UAAU,GAAG,UAAU,CAErF;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,EAAE,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,GAAG,SAAS,CAmC7H;AAaD,wBAAgB,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,GAAG,UAAU,CAMnG;AAoHD,QAAA,MAAM,cAAc;;;;;CAKV,CAAC;AAEX,KAAK,WAAW,GAAG,MAAM,OAAO,cAAc,CAAC"}
|
package/dist/x509.js
CHANGED
|
@@ -43,6 +43,9 @@ export function keyUsageExtension(usages) {
|
|
|
43
43
|
export function extendedKeyUsageClientAuthExtension() {
|
|
44
44
|
return extension(OID.extendedKeyUsage, false, sequence(oid(OID.clientAuth)));
|
|
45
45
|
}
|
|
46
|
+
export function extendedKeyUsageDocumentSigningExtension() {
|
|
47
|
+
return extension(OID.extendedKeyUsage, false, sequence(oid(OID.documentSigning)));
|
|
48
|
+
}
|
|
46
49
|
export function subjectKeyIdentifierExtension(keyIdentifier) {
|
|
47
50
|
return extension(OID.subjectKeyIdentifier, false, octetString(keyIdentifier));
|
|
48
51
|
}
|
|
@@ -90,7 +93,7 @@ function assertOptionalStringArray(name, values) {
|
|
|
90
93
|
throw new Error(`${name} must be an array`);
|
|
91
94
|
}
|
|
92
95
|
}
|
|
93
|
-
function extension(extensionOid, critical, valueDer) {
|
|
96
|
+
export function extension(extensionOid, critical, valueDer) {
|
|
94
97
|
return sequence(oid(extensionOid), ...(critical ? [boolean(true)] : []), octetString(valueDer));
|
|
95
98
|
}
|
|
96
99
|
function encodeSerialNumber(serialNumber) {
|
|
@@ -192,6 +195,7 @@ function assertYearInRange(year, fieldName) {
|
|
|
192
195
|
}
|
|
193
196
|
const KEY_USAGE_BITS = {
|
|
194
197
|
digitalSignature: 0,
|
|
198
|
+
contentCommitment: 1,
|
|
195
199
|
keyCertSign: 5,
|
|
196
200
|
cRLSign: 6
|
|
197
201
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noz-ele/edgca",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "A small Cloudflare Workers-friendly toolkit for issuing self-managed CA and mTLS client certificates.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -49,6 +49,9 @@
|
|
|
49
49
|
}
|
|
50
50
|
},
|
|
51
51
|
"types": "./dist/index.d.ts",
|
|
52
|
+
"bin": {
|
|
53
|
+
"edgca": "./dist/cli.js"
|
|
54
|
+
},
|
|
52
55
|
"scripts": {
|
|
53
56
|
"build": "tsc -p tsconfig.json",
|
|
54
57
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
@@ -61,6 +64,7 @@
|
|
|
61
64
|
},
|
|
62
65
|
"devDependencies": {
|
|
63
66
|
"@cloudflare/vitest-pool-workers": "^0.15.2",
|
|
67
|
+
"@types/node": "^25.7.0",
|
|
64
68
|
"@vitest/coverage-istanbul": "^4.1.5",
|
|
65
69
|
"fast-check": "^4.7.0",
|
|
66
70
|
"typescript": "^5.9.3",
|