@noz-ele/edgca 0.5.1 → 0.5.2
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 +1 -1
- package/dist/parser.d.ts +1 -0
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +38 -0
- package/dist/pkcs12.js +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ EdgCA is a small TypeScript library for issuing mTLS client certificates and doc
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
9
|
- **WebCrypto-only, zero runtime dependencies.** All cryptographic operations go through `globalThis.crypto.subtle`. The same code runs on Cloudflare Workers, Node.js 20+, and modern browsers without polyfills or bundler shims.
|
|
10
|
-
- **Lightweight.** v0.5.
|
|
10
|
+
- **Lightweight.** v0.5.2 — tarball **45.2 kB** · unpacked **167.3 kB** · 73 files. No transitive dependencies; the CLI uses only `node:util.parseArgs`. (Re-measured on every release.)
|
|
11
11
|
- **CA hierarchy (two-level).** Create a self-signed root CA and, optionally, issue an intermediate CA from it. Three or more levels of intermediates are intentionally out of scope.
|
|
12
12
|
- **PFX (PKCS#12) bundling.** Wrap a cert + private key (and optional chain) into a password-protected `.pfx` / `.p12` for OS keystore import (Win11+, macOS 15+, iOS/iPadOS 18+, modern Linux). Algorithm-agnostic — accepts arbitrary PKCS#8 DER bytes (ECDSA, RSA, Ed25519, …).
|
|
13
13
|
- **mTLS client certificate issuance.** Issue a leaf with internal key generation, or from a caller-managed key via the CSR path below.
|
package/dist/parser.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export interface ParsedCertificate {
|
|
|
12
12
|
subjectKeyIdentifier?: Uint8Array;
|
|
13
13
|
authorityKeyIdentifier?: Uint8Array;
|
|
14
14
|
}
|
|
15
|
+
export declare function extractCertificateSpkiDer(der: Uint8Array): Uint8Array;
|
|
15
16
|
export declare function parseCertificateDer(der: Uint8Array): Promise<ParsedCertificate>;
|
|
16
17
|
export declare function assertIssuerSubjectMatches(issuer: ParsedCertificate, issued: ParsedCertificate): void;
|
|
17
18
|
//# sourceMappingURL=parser.d.ts.map
|
package/dist/parser.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,UAAU,CAAC;IAChB,iBAAiB,EAAE,UAAU,CAAC;IAC9B,YAAY,EAAE,UAAU,CAAC;IACzB,aAAa,EAAE,UAAU,CAAC;IAC1B,cAAc,EAAE,UAAU,CAAC;IAC3B,uBAAuB,EAAE,UAAU,CAAC;IACpC,SAAS,EAAE,SAAS,CAAC;IACrB,IAAI,EAAE,OAAO,CAAC;IACd,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,EAAE,OAAO,CAAC;IACrB,oBAAoB,CAAC,EAAE,UAAU,CAAC;IAClC,sBAAsB,CAAC,EAAE,UAAU,CAAC;CACrC;AAED,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAkErF;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,iBAAiB,EAAE,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAIrG"}
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,UAAU,CAAC;IAChB,iBAAiB,EAAE,UAAU,CAAC;IAC9B,YAAY,EAAE,UAAU,CAAC;IACzB,aAAa,EAAE,UAAU,CAAC;IAC1B,cAAc,EAAE,UAAU,CAAC;IAC3B,uBAAuB,EAAE,UAAU,CAAC;IACpC,SAAS,EAAE,SAAS,CAAC;IACrB,IAAI,EAAE,OAAO,CAAC;IACd,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,EAAE,OAAO,CAAC;IACrB,oBAAoB,CAAC,EAAE,UAAU,CAAC;IAClC,sBAAsB,CAAC,EAAE,UAAU,CAAC;CACrC;AAQD,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,UAAU,GAAG,UAAU,CA+BrE;AAED,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAkErF;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,iBAAiB,EAAE,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAIrG"}
|
package/dist/parser.js
CHANGED
|
@@ -2,6 +2,44 @@ import { bytesEqual } from "./bytes.js";
|
|
|
2
2
|
import { decodeInteger, decodeOid, readChildren, readElement, readSequenceChildren, TAG } from "./der.js";
|
|
3
3
|
import { importPublicKeySpki } from "./crypto.js";
|
|
4
4
|
import { OID } from "./oids.js";
|
|
5
|
+
// Read just the SubjectPublicKeyInfo DER from a v3 X.509 certificate without
|
|
6
|
+
// importing the public key. Used by exportPkcs12, which needs the SPKI for
|
|
7
|
+
// localKeyId computation (SHA-1 of the BIT STRING value) regardless of the
|
|
8
|
+
// inner key algorithm. parseCertificateDer below additionally imports the
|
|
9
|
+
// SPKI as an ECDSA CryptoKey for the issuance/verify paths; that import is
|
|
10
|
+
// EC-only and must not be on the pkcs12 path.
|
|
11
|
+
export function extractCertificateSpkiDer(der) {
|
|
12
|
+
const certificate = readElement(der);
|
|
13
|
+
if (certificate.tag !== TAG.SEQUENCE || certificate.end !== der.length) {
|
|
14
|
+
throw new Error("Invalid certificate DER");
|
|
15
|
+
}
|
|
16
|
+
const [tbsCertificate] = readSequenceChildren(certificate);
|
|
17
|
+
if (!tbsCertificate) {
|
|
18
|
+
throw new Error("Invalid certificate structure");
|
|
19
|
+
}
|
|
20
|
+
const tbsChildren = readSequenceChildren(tbsCertificate);
|
|
21
|
+
const versionTag = tbsChildren[0];
|
|
22
|
+
if (!versionTag || versionTag.tag !== 0xa0) {
|
|
23
|
+
throw new Error("Unsupported X.509 version (only v3 is supported)");
|
|
24
|
+
}
|
|
25
|
+
const versionInner = readElement(versionTag.value);
|
|
26
|
+
if (versionInner.tag !== TAG.INTEGER || decodeInteger(versionInner.value) !== 2n) {
|
|
27
|
+
throw new Error("Unsupported X.509 version (only v3 is supported)");
|
|
28
|
+
}
|
|
29
|
+
// tbsCertificate (v3) layout:
|
|
30
|
+
// [0] version [0] EXPLICIT INTEGER 2
|
|
31
|
+
// [1] serialNumber
|
|
32
|
+
// [2] signature (AlgorithmIdentifier)
|
|
33
|
+
// [3] issuer
|
|
34
|
+
// [4] validity
|
|
35
|
+
// [5] subject
|
|
36
|
+
// [6] subjectPublicKeyInfo
|
|
37
|
+
const subjectPublicKeyInfo = tbsChildren[6];
|
|
38
|
+
if (!subjectPublicKeyInfo || subjectPublicKeyInfo.tag !== TAG.SEQUENCE) {
|
|
39
|
+
throw new Error("Invalid certificate: missing subjectPublicKeyInfo");
|
|
40
|
+
}
|
|
41
|
+
return subjectPublicKeyInfo.raw;
|
|
42
|
+
}
|
|
5
43
|
export async function parseCertificateDer(der) {
|
|
6
44
|
const certificate = readElement(der);
|
|
7
45
|
if (certificate.tag !== TAG.SEQUENCE || certificate.end !== der.length) {
|
package/dist/pkcs12.js
CHANGED
|
@@ -2,7 +2,7 @@ import { arrayBufferFromBytes, concatBytes, randomBytes } from "./bytes.js";
|
|
|
2
2
|
import { digestSha256, keyIdentifierFromSpki } from "./crypto.js";
|
|
3
3
|
import { contextPrimitive, der, explicit, integer, nullValue, octetString, oid, sequence, set } from "./der.js";
|
|
4
4
|
import { OID } from "./oids.js";
|
|
5
|
-
import {
|
|
5
|
+
import { extractCertificateSpkiDer } from "./parser.js";
|
|
6
6
|
const DEFAULT_PBKDF2_ITERATIONS = 600_000;
|
|
7
7
|
const DEFAULT_MAC_ITERATIONS = 100_000;
|
|
8
8
|
const SALT_LENGTH = 16;
|
|
@@ -14,8 +14,8 @@ export async function exportPkcs12(input) {
|
|
|
14
14
|
const iterations = input.iterations ?? DEFAULT_PBKDF2_ITERATIONS;
|
|
15
15
|
const macIterations = input.macIterations ?? DEFAULT_MAC_ITERATIONS;
|
|
16
16
|
validateInput(input, iterations, macIterations);
|
|
17
|
-
const
|
|
18
|
-
const localKeyId = await keyIdentifierFromSpki(
|
|
17
|
+
const spkiDer = extractCertificateSpkiDer(input.certDer);
|
|
18
|
+
const localKeyId = await keyIdentifierFromSpki(spkiDer);
|
|
19
19
|
const friendlyNameAttr = input.friendlyName !== undefined && input.friendlyName.length > 0
|
|
20
20
|
? buildFriendlyNameAttr(input.friendlyName)
|
|
21
21
|
: undefined;
|