@noz-ele/edgca 0.1.0 → 0.3.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 +385 -270
- package/dist/bytes.d.ts +1 -0
- package/dist/bytes.d.ts.map +1 -1
- package/dist/bytes.js +5 -0
- package/dist/ca.d.ts +2 -1
- package/dist/ca.d.ts.map +1 -1
- package/dist/ca.js +63 -34
- package/dist/crypto.d.ts +7 -7
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +98 -44
- package/dist/csr.d.ts +25 -0
- package/dist/csr.d.ts.map +1 -0
- package/dist/csr.js +266 -0
- package/dist/der.d.ts +1 -0
- package/dist/der.d.ts.map +1 -1
- package/dist/der.js +3 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/oids.d.ts +19 -0
- package/dist/oids.d.ts.map +1 -1
- package/dist/oids.js +20 -1
- package/dist/pem.d.ts +0 -2
- package/dist/pem.d.ts.map +1 -1
- package/dist/pem.js +0 -6
- package/dist/pkcs12.d.ts +11 -0
- package/dist/pkcs12.d.ts.map +1 -0
- package/dist/pkcs12.js +248 -0
- package/dist/types.d.ts +18 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/x509.d.ts +4 -2
- package/dist/x509.d.ts.map +1 -1
- package/dist/x509.js +8 -8
- package/package.json +70 -66
package/dist/bytes.d.ts
CHANGED
|
@@ -6,4 +6,5 @@ export declare function bytesToBinary(bytes: Uint8Array): string;
|
|
|
6
6
|
export declare function binaryToBytes(binary: string): Uint8Array;
|
|
7
7
|
export declare function cloneBytes(bytes: Uint8Array): Uint8Array;
|
|
8
8
|
export declare function arrayBufferFromBytes(bytes: Uint8Array): ArrayBuffer;
|
|
9
|
+
export declare function randomBytes(length: number): Uint8Array;
|
|
9
10
|
//# sourceMappingURL=bytes.d.ts.map
|
package/dist/bytes.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bytes.d.ts","sourceRoot":"","sources":["../src/bytes.ts"],"names":[],"mappings":"AAAA,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,GAAG,UAAU,CAWpE;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,OAAO,CAWhE;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAEnD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAYpD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAUvD;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAQxD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,CAExD;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,UAAU,GAAG,WAAW,CAInE"}
|
|
1
|
+
{"version":3,"file":"bytes.d.ts","sourceRoot":"","sources":["../src/bytes.ts"],"names":[],"mappings":"AAAA,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,GAAG,UAAU,CAWpE;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,OAAO,CAWhE;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAEnD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAYpD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAUvD;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAQxD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,CAExD;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,UAAU,GAAG,WAAW,CAInE;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAItD"}
|
package/dist/bytes.js
CHANGED
package/dist/ca.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { CertificateAuthority, CreateRootCAOptions, ImportCertificateAuthorityOptions, IssueClientCertOptions, IssueIntermediateCAOptions, IssuedClientCertificate } from "./types.js";
|
|
1
|
+
import type { CertificateAuthority, CreateRootCAOptions, ImportCertificateAuthorityOptions, IssueClientCertForPublicKeyOptions, IssueClientCertOptions, IssueIntermediateCAOptions, IssuedClientCertificate, IssuedClientCertificateForPublicKey } 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
|
+
export declare function issueClientCertForPublicKey(options: IssueClientCertForPublicKeyOptions): Promise<IssuedClientCertificateForPublicKey>;
|
|
5
6
|
export declare function importCertificateAuthority(options: ImportCertificateAuthorityOptions): Promise<CertificateAuthority>;
|
|
6
7
|
//# 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":"
|
|
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,EAEpC,MAAM,YAAY,CAAC;AAapB,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,CAqBvG;AAED,wBAAsB,2BAA2B,CAC/C,OAAO,EAAE,kCAAkC,GAC1C,OAAO,CAAC,mCAAmC,CAAC,CAkB9C;AAuDD,wBAAsB,0BAA0B,CAAC,OAAO,EAAE,iCAAiC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAe1H"}
|
package/dist/ca.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { bytesEqual, cloneBytes } from "./bytes.js";
|
|
2
|
-
import { assertKeyPairMatches, exportSpki, generateKeyPair,
|
|
2
|
+
import { assertKeyPairMatches, curveOf, exportSpki, generateKeyPair, keyIdentifierFromSpki, signDer } from "./crypto.js";
|
|
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
6
|
import { authorityKeyIdentifierExtension, basicConstraintsCaExtension, basicConstraintsLeafExtension, buildCertificate, buildTbsCertificate, extendedKeyUsageClientAuthExtension, keyUsageExtension, subjectAltNameExtension, subjectKeyIdentifierExtension } from "./x509.js";
|
|
7
7
|
export async function createRootCA(options) {
|
|
8
|
-
const keyPair = await resolveKeyPair(options.
|
|
8
|
+
const keyPair = await resolveKeyPair(options.keyPair);
|
|
9
|
+
const issuerCurve = curveOf(keyPair.privateKey);
|
|
9
10
|
const subjectNameDer = encodeName(options.subject);
|
|
10
11
|
const spki = await exportSpki(keyPair.publicKey);
|
|
11
12
|
const keyIdentifier = await keyIdentifierFromSpki(spki);
|
|
@@ -23,10 +24,11 @@ export async function createRootCA(options) {
|
|
|
23
24
|
issuerNameDer: subjectNameDer,
|
|
24
25
|
subjectNameDer,
|
|
25
26
|
subjectPublicKeyInfoDer: spki,
|
|
26
|
-
extensions
|
|
27
|
+
extensions,
|
|
28
|
+
issuerCurve
|
|
27
29
|
});
|
|
28
30
|
const signatureDer = await signDer(keyPair.privateKey, tbsCertificateDer);
|
|
29
|
-
const certDer = buildCertificate(tbsCertificateDer, signatureDer);
|
|
31
|
+
const certDer = buildCertificate(tbsCertificateDer, signatureDer, issuerCurve);
|
|
30
32
|
return assembleCertificateAuthority(certDer, keyPair, "");
|
|
31
33
|
}
|
|
32
34
|
export async function issueIntermediateCA(options) {
|
|
@@ -34,7 +36,8 @@ export async function issueIntermediateCA(options) {
|
|
|
34
36
|
const issuerChainPem = options.ca.issuerChainPem;
|
|
35
37
|
assertCanIssueCertificate(issuer);
|
|
36
38
|
assertCanIssueIntermediate(issuer, issuerChainPem, options.pathLenConstraint);
|
|
37
|
-
const
|
|
39
|
+
const issuerCurve = curveOf(options.ca.privateKey);
|
|
40
|
+
const keyPair = await resolveKeyPair(options.keyPair);
|
|
38
41
|
const subjectNameDer = encodeName(options.subject);
|
|
39
42
|
const spki = await exportSpki(keyPair.publicKey);
|
|
40
43
|
const subjectKeyIdentifier = await keyIdentifierFromSpki(spki);
|
|
@@ -52,23 +55,57 @@ export async function issueIntermediateCA(options) {
|
|
|
52
55
|
issuerNameDer: issuer.subjectNameDer,
|
|
53
56
|
subjectNameDer,
|
|
54
57
|
subjectPublicKeyInfoDer: spki,
|
|
55
|
-
extensions
|
|
58
|
+
extensions,
|
|
59
|
+
issuerCurve
|
|
56
60
|
});
|
|
57
61
|
const signatureDer = await signDer(options.ca.privateKey, tbsCertificateDer);
|
|
58
|
-
const certDer = buildCertificate(tbsCertificateDer, signatureDer);
|
|
62
|
+
const certDer = buildCertificate(tbsCertificateDer, signatureDer, issuerCurve);
|
|
59
63
|
const childChainPem = joinPemChain([options.ca.certPem, issuerChainPem]);
|
|
60
64
|
return assembleCertificateAuthority(certDer, keyPair, childChainPem);
|
|
61
65
|
}
|
|
62
66
|
export async function issueClientCert(options) {
|
|
63
|
-
const issuer = await parseIssuer(options.ca);
|
|
64
|
-
const issuerChainPem = options.ca.issuerChainPem;
|
|
65
|
-
assertCanIssueCertificate(issuer);
|
|
66
67
|
const keyPair = await generateKeyPair();
|
|
67
|
-
const
|
|
68
|
-
|
|
68
|
+
const built = await buildClientCertificate(options.ca, keyPair.publicKey, {
|
|
69
|
+
subject: options.subject,
|
|
70
|
+
days: options.days,
|
|
71
|
+
notBefore: options.notBefore,
|
|
72
|
+
serialNumber: options.serialNumber,
|
|
73
|
+
dnsNames: options.dnsNames,
|
|
74
|
+
ipAddresses: options.ipAddresses
|
|
75
|
+
});
|
|
76
|
+
return {
|
|
77
|
+
certPem: built.certPem,
|
|
78
|
+
certDer: built.certDer,
|
|
79
|
+
privateKey: keyPair.privateKey,
|
|
80
|
+
publicKey: keyPair.publicKey,
|
|
81
|
+
certChainPem: built.certChainPem
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
export async function issueClientCertForPublicKey(options) {
|
|
85
|
+
const built = await buildClientCertificate(options.ca, options.publicKey, {
|
|
86
|
+
subject: options.subject,
|
|
87
|
+
days: options.days,
|
|
88
|
+
notBefore: options.notBefore,
|
|
89
|
+
serialNumber: options.serialNumber,
|
|
90
|
+
dnsNames: options.dnsNames,
|
|
91
|
+
ipAddresses: options.ipAddresses
|
|
92
|
+
});
|
|
93
|
+
return {
|
|
94
|
+
certPem: built.certPem,
|
|
95
|
+
certDer: built.certDer,
|
|
96
|
+
certChainPem: built.certChainPem
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
async function buildClientCertificate(ca, subjectPublicKey, content) {
|
|
100
|
+
const issuer = await parseIssuer(ca);
|
|
101
|
+
const issuerChainPem = ca.issuerChainPem;
|
|
102
|
+
assertCanIssueCertificate(issuer);
|
|
103
|
+
const issuerCurve = curveOf(ca.privateKey);
|
|
104
|
+
const subjectNameDer = encodeName(content.subject);
|
|
105
|
+
const spki = await exportSpki(subjectPublicKey);
|
|
69
106
|
const subjectKeyIdentifier = await keyIdentifierFromSpki(spki);
|
|
70
107
|
const authorityKeyIdentifier = issuer.subjectKeyIdentifier ?? await keyIdentifierFromSpki(issuer.subjectPublicKeyInfoDer);
|
|
71
|
-
const san = subjectAltNameExtension(
|
|
108
|
+
const san = subjectAltNameExtension(content.dnsNames, content.ipAddresses);
|
|
72
109
|
const extensions = [
|
|
73
110
|
basicConstraintsLeafExtension(),
|
|
74
111
|
keyUsageExtension(["digitalSignature"]),
|
|
@@ -78,25 +115,22 @@ export async function issueClientCert(options) {
|
|
|
78
115
|
...(san ? [san] : [])
|
|
79
116
|
];
|
|
80
117
|
const { tbsCertificateDer } = buildTbsCertificate({
|
|
81
|
-
serialNumber:
|
|
82
|
-
notBefore:
|
|
83
|
-
days:
|
|
118
|
+
serialNumber: content.serialNumber,
|
|
119
|
+
notBefore: content.notBefore,
|
|
120
|
+
days: content.days,
|
|
84
121
|
issuerNameDer: issuer.subjectNameDer,
|
|
85
122
|
subjectNameDer,
|
|
86
123
|
subjectPublicKeyInfoDer: spki,
|
|
87
|
-
extensions
|
|
124
|
+
extensions,
|
|
125
|
+
issuerCurve
|
|
88
126
|
});
|
|
89
|
-
const signatureDer = await signDer(
|
|
90
|
-
const certDer = buildCertificate(tbsCertificateDer, signatureDer);
|
|
127
|
+
const signatureDer = await signDer(ca.privateKey, tbsCertificateDer);
|
|
128
|
+
const certDer = buildCertificate(tbsCertificateDer, signatureDer, issuerCurve);
|
|
91
129
|
const certPem = certificateToPem(certDer);
|
|
92
130
|
return {
|
|
93
131
|
certPem,
|
|
94
|
-
privateKeyPem: await privateKeyToPem(keyPair.privateKey),
|
|
95
|
-
publicKeyPem: await publicKeyToPem(keyPair.publicKey),
|
|
96
132
|
certDer: cloneBytes(certDer),
|
|
97
|
-
|
|
98
|
-
publicKey: keyPair.publicKey,
|
|
99
|
-
certChainPem: joinPemChain([certPem, options.ca.certPem, issuerChainPem])
|
|
133
|
+
certChainPem: joinPemChain([certPem, ca.certPem, issuerChainPem])
|
|
100
134
|
};
|
|
101
135
|
}
|
|
102
136
|
export async function importCertificateAuthority(options) {
|
|
@@ -104,14 +138,11 @@ export async function importCertificateAuthority(options) {
|
|
|
104
138
|
assertIssuerChainPem(issuerChainPem);
|
|
105
139
|
const certDer = pemToDerWithLabel(options.certPem, "CERTIFICATE");
|
|
106
140
|
const parsed = await parseCertificateDer(certDer);
|
|
107
|
-
|
|
108
|
-
await assertKeyPairMatches(privateKey, parsed.publicKey);
|
|
141
|
+
await assertKeyPairMatches(options.privateKey, parsed.publicKey);
|
|
109
142
|
return {
|
|
110
143
|
certPem: options.certPem,
|
|
111
|
-
privateKeyPem: options.privateKeyPem,
|
|
112
|
-
publicKeyPem: await publicKeyToPem(parsed.publicKey),
|
|
113
144
|
certDer: cloneBytes(certDer),
|
|
114
|
-
privateKey,
|
|
145
|
+
privateKey: options.privateKey,
|
|
115
146
|
publicKey: parsed.publicKey,
|
|
116
147
|
issuerChainPem
|
|
117
148
|
};
|
|
@@ -128,17 +159,15 @@ function assertIssuerChainPem(chainPem) {
|
|
|
128
159
|
pemToDerWithLabel(block, "CERTIFICATE");
|
|
129
160
|
}
|
|
130
161
|
}
|
|
131
|
-
async function resolveKeyPair(
|
|
132
|
-
if (
|
|
133
|
-
return
|
|
162
|
+
async function resolveKeyPair(provided) {
|
|
163
|
+
if (provided !== undefined) {
|
|
164
|
+
return provided;
|
|
134
165
|
}
|
|
135
166
|
return generateKeyPair();
|
|
136
167
|
}
|
|
137
168
|
async function assembleCertificateAuthority(certDer, keyPair, issuerChainPem) {
|
|
138
169
|
return {
|
|
139
170
|
certPem: certificateToPem(certDer),
|
|
140
|
-
privateKeyPem: await privateKeyToPem(keyPair.privateKey),
|
|
141
|
-
publicKeyPem: await publicKeyToPem(keyPair.publicKey),
|
|
142
171
|
certDer: cloneBytes(certDer),
|
|
143
172
|
privateKey: keyPair.privateKey,
|
|
144
173
|
publicKey: keyPair.publicKey,
|
package/dist/crypto.d.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type SupportedCurve = "P-256" | "P-384" | "P-521";
|
|
2
|
+
export declare function curveOf(key: CryptoKey): SupportedCurve;
|
|
3
|
+
export declare function componentSizeForCurve(curve: SupportedCurve): number;
|
|
4
|
+
export declare function signatureAlgorithmOidForCurve(curve: SupportedCurve): string;
|
|
5
|
+
export declare function generateKeyPair(curve?: SupportedCurve): Promise<CryptoKeyPair>;
|
|
2
6
|
export declare function signDer(privateKey: CryptoKey, data: Uint8Array): Promise<Uint8Array>;
|
|
3
7
|
export declare function verifyDer(publicKey: CryptoKey, signatureDer: Uint8Array, data: Uint8Array): Promise<boolean>;
|
|
4
8
|
export declare function digestSha256(data: Uint8Array): Promise<Uint8Array>;
|
|
5
9
|
export declare function digestSha1(data: Uint8Array): Promise<Uint8Array>;
|
|
6
10
|
export declare function keyIdentifierFromSpki(spki: Uint8Array): Promise<Uint8Array>;
|
|
7
|
-
export declare function privateKeyToPem(key: CryptoKey): Promise<string>;
|
|
8
|
-
export declare function publicKeyToPem(key: CryptoKey): Promise<string>;
|
|
9
11
|
export declare function exportSpki(key: CryptoKey): Promise<Uint8Array>;
|
|
10
|
-
export declare function importPrivateKeyPem(pem: string): Promise<CryptoKey>;
|
|
11
|
-
export declare function keyPairFromPrivateKeyPem(pem: string): Promise<CryptoKeyPair>;
|
|
12
12
|
export declare function importPublicKeySpki(spki: Uint8Array): Promise<CryptoKey>;
|
|
13
13
|
export declare function assertKeyPairMatches(privateKey: CryptoKey, publicKey: CryptoKey): Promise<void>;
|
|
14
|
-
export declare function ecdsaRawToDer(raw: Uint8Array): Uint8Array;
|
|
15
|
-
export declare function ecdsaDerToRaw(signature: Uint8Array): Uint8Array;
|
|
14
|
+
export declare function ecdsaRawToDer(raw: Uint8Array, componentSize: number): Uint8Array;
|
|
15
|
+
export declare function ecdsaDerToRaw(signature: Uint8Array, componentSize: number): Uint8Array;
|
|
16
16
|
//# sourceMappingURL=crypto.d.ts.map
|
package/dist/crypto.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AAkCzD,wBAAgB,OAAO,CAAC,GAAG,EAAE,SAAS,GAAG,cAAc,CAUtD;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAEnE;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAE3E;AAED,wBAAsB,eAAe,CAAC,KAAK,GAAE,cAAwB,GAAG,OAAO,CAAC,aAAa,CAAC,CAE7F;AAED,wBAAsB,OAAO,CAAC,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAO1F;AAED,wBAAsB,SAAS,CAAC,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CASlH;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAExE;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAEtE;AAID,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAWjF;AAED,wBAAsB,UAAU,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAEpE;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAS9E;AAkCD,wBAAsB,oBAAoB,CAAC,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAQrG;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,GAAG,UAAU,CAMhF;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,GAAG,UAAU,CAetF"}
|
package/dist/crypto.js
CHANGED
|
@@ -1,23 +1,61 @@
|
|
|
1
1
|
import { arrayBufferFromBytes, concatBytes } from "./bytes.js";
|
|
2
|
-
import { integer, readChildren, readElement, readSequenceChildren, sequence, TAG } from "./der.js";
|
|
3
|
-
import {
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
import { decodeOid, integer, readChildren, readElement, readSequenceChildren, sequence, TAG } from "./der.js";
|
|
3
|
+
import { OID } from "./oids.js";
|
|
4
|
+
const CURVE_PROFILE = {
|
|
5
|
+
"P-256": {
|
|
6
|
+
componentSize: 32,
|
|
7
|
+
hash: "SHA-256",
|
|
8
|
+
signatureAlgorithmOid: OID.ecdsaWithSha256,
|
|
9
|
+
curveOid: OID.secp256r1
|
|
10
|
+
},
|
|
11
|
+
"P-384": {
|
|
12
|
+
componentSize: 48,
|
|
13
|
+
hash: "SHA-384",
|
|
14
|
+
signatureAlgorithmOid: OID.ecdsaWithSha384,
|
|
15
|
+
curveOid: OID.secp384r1
|
|
16
|
+
},
|
|
17
|
+
"P-521": {
|
|
18
|
+
componentSize: 66,
|
|
19
|
+
hash: "SHA-512",
|
|
20
|
+
signatureAlgorithmOid: OID.ecdsaWithSha512,
|
|
21
|
+
curveOid: OID.secp521r1
|
|
22
|
+
}
|
|
7
23
|
};
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
24
|
+
const CURVE_OID_TO_NAME = {
|
|
25
|
+
[OID.secp256r1]: "P-256",
|
|
26
|
+
[OID.secp384r1]: "P-384",
|
|
27
|
+
[OID.secp521r1]: "P-521"
|
|
11
28
|
};
|
|
12
|
-
export
|
|
13
|
-
|
|
29
|
+
export function curveOf(key) {
|
|
30
|
+
const algorithm = key.algorithm;
|
|
31
|
+
if (algorithm.name !== "ECDSA") {
|
|
32
|
+
throw new Error(`Expected ECDSA key, got ${algorithm.name}`);
|
|
33
|
+
}
|
|
34
|
+
const curve = algorithm.namedCurve;
|
|
35
|
+
if (curve !== "P-256" && curve !== "P-384" && curve !== "P-521") {
|
|
36
|
+
throw new Error(`Unsupported ECDSA curve: ${curve}`);
|
|
37
|
+
}
|
|
38
|
+
return curve;
|
|
39
|
+
}
|
|
40
|
+
export function componentSizeForCurve(curve) {
|
|
41
|
+
return CURVE_PROFILE[curve].componentSize;
|
|
42
|
+
}
|
|
43
|
+
export function signatureAlgorithmOidForCurve(curve) {
|
|
44
|
+
return CURVE_PROFILE[curve].signatureAlgorithmOid;
|
|
45
|
+
}
|
|
46
|
+
export async function generateKeyPair(curve = "P-256") {
|
|
47
|
+
return crypto.subtle.generateKey({ name: "ECDSA", namedCurve: curve }, true, ["sign", "verify"]);
|
|
14
48
|
}
|
|
15
49
|
export async function signDer(privateKey, data) {
|
|
16
|
-
const
|
|
17
|
-
|
|
50
|
+
const curve = curveOf(privateKey);
|
|
51
|
+
const profile = CURVE_PROFILE[curve];
|
|
52
|
+
const raw = new Uint8Array(await crypto.subtle.sign({ name: "ECDSA", hash: profile.hash }, privateKey, arrayBufferFromBytes(data)));
|
|
53
|
+
return ecdsaRawToDer(raw, profile.componentSize);
|
|
18
54
|
}
|
|
19
55
|
export async function verifyDer(publicKey, signatureDer, data) {
|
|
20
|
-
|
|
56
|
+
const curve = curveOf(publicKey);
|
|
57
|
+
const profile = CURVE_PROFILE[curve];
|
|
58
|
+
return crypto.subtle.verify({ name: "ECDSA", hash: profile.hash }, publicKey, arrayBufferFromBytes(ecdsaDerToRaw(signatureDer, profile.componentSize)), arrayBufferFromBytes(data));
|
|
21
59
|
}
|
|
22
60
|
export async function digestSha256(data) {
|
|
23
61
|
return new Uint8Array(await crypto.subtle.digest("SHA-256", arrayBufferFromBytes(data)));
|
|
@@ -39,30 +77,43 @@ export async function keyIdentifierFromSpki(spki) {
|
|
|
39
77
|
}
|
|
40
78
|
return digestSha1(subjectPublicKey.value.subarray(1));
|
|
41
79
|
}
|
|
42
|
-
export async function privateKeyToPem(key) {
|
|
43
|
-
const der = new Uint8Array(await crypto.subtle.exportKey("pkcs8", key));
|
|
44
|
-
return privateKeyDerToPem(der);
|
|
45
|
-
}
|
|
46
|
-
export async function publicKeyToPem(key) {
|
|
47
|
-
const der = new Uint8Array(await crypto.subtle.exportKey("spki", key));
|
|
48
|
-
return publicKeyDerToPem(der);
|
|
49
|
-
}
|
|
50
80
|
export async function exportSpki(key) {
|
|
51
81
|
return new Uint8Array(await crypto.subtle.exportKey("spki", key));
|
|
52
82
|
}
|
|
53
|
-
export async function importPrivateKeyPem(pem) {
|
|
54
|
-
return crypto.subtle.importKey("pkcs8", arrayBufferFromBytes(pemToDerWithLabel(pem, "PRIVATE KEY")), EC_ALGORITHM, true, ["sign"]);
|
|
55
|
-
}
|
|
56
|
-
export async function keyPairFromPrivateKeyPem(pem) {
|
|
57
|
-
const privateKey = await importPrivateKeyPem(pem);
|
|
58
|
-
const jwk = await crypto.subtle.exportKey("jwk", privateKey);
|
|
59
|
-
delete jwk.d;
|
|
60
|
-
jwk.key_ops = ["verify"];
|
|
61
|
-
const publicKey = await crypto.subtle.importKey("jwk", jwk, EC_ALGORITHM, true, ["verify"]);
|
|
62
|
-
return { privateKey, publicKey };
|
|
63
|
-
}
|
|
64
83
|
export async function importPublicKeySpki(spki) {
|
|
65
|
-
|
|
84
|
+
const curve = curveFromSpki(spki);
|
|
85
|
+
return crypto.subtle.importKey("spki", arrayBufferFromBytes(spki), { name: "ECDSA", namedCurve: curve }, true, ["verify"]);
|
|
86
|
+
}
|
|
87
|
+
// SubjectPublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier, subjectPublicKey BIT STRING }
|
|
88
|
+
// AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY }
|
|
89
|
+
// For EC keys, parameters is the named-curve OID.
|
|
90
|
+
function curveFromSpki(spki) {
|
|
91
|
+
const root = readElement(spki);
|
|
92
|
+
if (root.tag !== TAG.SEQUENCE) {
|
|
93
|
+
throw new Error("Invalid SubjectPublicKeyInfo");
|
|
94
|
+
}
|
|
95
|
+
const algorithm = readSequenceChildren(root)[0];
|
|
96
|
+
if (!algorithm || algorithm.tag !== TAG.SEQUENCE) {
|
|
97
|
+
throw new Error("Invalid SubjectPublicKeyInfo algorithm");
|
|
98
|
+
}
|
|
99
|
+
const algorithmChildren = readSequenceChildren(algorithm);
|
|
100
|
+
const algorithmOidElement = algorithmChildren[0];
|
|
101
|
+
const parametersElement = algorithmChildren[1];
|
|
102
|
+
if (!algorithmOidElement || algorithmOidElement.tag !== TAG.OBJECT_IDENTIFIER) {
|
|
103
|
+
throw new Error("Invalid SubjectPublicKeyInfo algorithm OID");
|
|
104
|
+
}
|
|
105
|
+
if (decodeOid(algorithmOidElement.value) !== OID.ecPublicKey) {
|
|
106
|
+
throw new Error("SubjectPublicKeyInfo is not an EC public key");
|
|
107
|
+
}
|
|
108
|
+
if (!parametersElement || parametersElement.tag !== TAG.OBJECT_IDENTIFIER) {
|
|
109
|
+
throw new Error("EC SubjectPublicKeyInfo parameters must be a named-curve OID");
|
|
110
|
+
}
|
|
111
|
+
const curveOid = decodeOid(parametersElement.value);
|
|
112
|
+
const curve = CURVE_OID_TO_NAME[curveOid];
|
|
113
|
+
if (!curve) {
|
|
114
|
+
throw new Error(`Unsupported EC named curve OID: ${curveOid}`);
|
|
115
|
+
}
|
|
116
|
+
return curve;
|
|
66
117
|
}
|
|
67
118
|
export async function assertKeyPairMatches(privateKey, publicKey) {
|
|
68
119
|
const data = new TextEncoder().encode("edgca-key-pair-check");
|
|
@@ -72,13 +123,13 @@ export async function assertKeyPairMatches(privateKey, publicKey) {
|
|
|
72
123
|
throw new Error("Private key does not match CA certificate public key");
|
|
73
124
|
}
|
|
74
125
|
}
|
|
75
|
-
export function ecdsaRawToDer(raw) {
|
|
76
|
-
if (raw.length !==
|
|
77
|
-
throw new Error(
|
|
126
|
+
export function ecdsaRawToDer(raw, componentSize) {
|
|
127
|
+
if (raw.length !== componentSize * 2) {
|
|
128
|
+
throw new Error(`ECDSA raw signature must be ${componentSize * 2} bytes`);
|
|
78
129
|
}
|
|
79
|
-
return sequence(integer(raw.subarray(0,
|
|
130
|
+
return sequence(integer(raw.subarray(0, componentSize)), integer(raw.subarray(componentSize)));
|
|
80
131
|
}
|
|
81
|
-
export function ecdsaDerToRaw(signature) {
|
|
132
|
+
export function ecdsaDerToRaw(signature, componentSize) {
|
|
82
133
|
const root = readElement(signature);
|
|
83
134
|
if (root.tag !== TAG.SEQUENCE || root.end !== signature.length) {
|
|
84
135
|
throw new Error("Invalid DER ECDSA signature");
|
|
@@ -87,18 +138,21 @@ export function ecdsaDerToRaw(signature) {
|
|
|
87
138
|
if (!r || !s || r.tag !== TAG.INTEGER || s.tag !== TAG.INTEGER) {
|
|
88
139
|
throw new Error("Invalid DER ECDSA signature integers");
|
|
89
140
|
}
|
|
90
|
-
return concatBytes([
|
|
141
|
+
return concatBytes([
|
|
142
|
+
integerToFixedWidth(r.value, componentSize),
|
|
143
|
+
integerToFixedWidth(s.value, componentSize)
|
|
144
|
+
]);
|
|
91
145
|
}
|
|
92
|
-
function integerToFixedWidth(value) {
|
|
146
|
+
function integerToFixedWidth(value, size) {
|
|
93
147
|
let start = 0;
|
|
94
148
|
while (start < value.length - 1 && value[start] === 0) {
|
|
95
149
|
start += 1;
|
|
96
150
|
}
|
|
97
151
|
const trimmed = value.subarray(start);
|
|
98
|
-
if (trimmed.length >
|
|
99
|
-
throw new Error(
|
|
152
|
+
if (trimmed.length > size) {
|
|
153
|
+
throw new Error(`ECDSA integer is wider than ${size} bytes`);
|
|
100
154
|
}
|
|
101
|
-
const out = new Uint8Array(
|
|
102
|
-
out.set(trimmed,
|
|
155
|
+
const out = new Uint8Array(size);
|
|
156
|
+
out.set(trimmed, size - trimmed.length);
|
|
103
157
|
return out;
|
|
104
158
|
}
|
package/dist/csr.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Subject } from "./types.js";
|
|
2
|
+
export interface CertificateSigningRequestExtension {
|
|
3
|
+
oid: string;
|
|
4
|
+
critical: boolean;
|
|
5
|
+
valueDer: Uint8Array;
|
|
6
|
+
}
|
|
7
|
+
export interface CertificateSigningRequestAttribute {
|
|
8
|
+
oid: string;
|
|
9
|
+
valuesDer: ReadonlyArray<Uint8Array>;
|
|
10
|
+
}
|
|
11
|
+
export interface ParsedCertificateSigningRequest {
|
|
12
|
+
subject: Subject;
|
|
13
|
+
publicKey: CryptoKey;
|
|
14
|
+
subjectPublicKeyInfoDer: Uint8Array;
|
|
15
|
+
requestedDnsNames: readonly string[];
|
|
16
|
+
requestedIpAddresses: readonly string[];
|
|
17
|
+
requestedExtensions: readonly CertificateSigningRequestExtension[];
|
|
18
|
+
otherAttributes: readonly CertificateSigningRequestAttribute[];
|
|
19
|
+
signatureAlgorithmOid: string;
|
|
20
|
+
signatureDer: Uint8Array;
|
|
21
|
+
certificationRequestInfoDer: Uint8Array;
|
|
22
|
+
}
|
|
23
|
+
export declare function parseCertificateSigningRequest(input: string | Uint8Array): Promise<ParsedCertificateSigningRequest>;
|
|
24
|
+
export declare function verifyCertificateSigningRequestSignature(csr: ParsedCertificateSigningRequest): Promise<boolean>;
|
|
25
|
+
//# sourceMappingURL=csr.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csr.d.ts","sourceRoot":"","sources":["../src/csr.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,OAAO,EAAwB,MAAM,YAAY,CAAC;AAkBhE,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"}
|