马宇豪
2024-07-16 f591c27b57e2418c9495bc02ae8cfff84d35bc18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.X509Certificate = exports.EXTENSION_OID_SCT = void 0;
/*
Copyright 2023 The Sigstore Authors.
 
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
 
    http://www.apache.org/licenses/LICENSE-2.0
 
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const asn1_1 = require("../asn1");
const crypto = __importStar(require("../crypto"));
const oid_1 = require("../oid");
const pem = __importStar(require("../pem"));
const ext_1 = require("./ext");
const EXTENSION_OID_SUBJECT_KEY_ID = '2.5.29.14';
const EXTENSION_OID_KEY_USAGE = '2.5.29.15';
const EXTENSION_OID_SUBJECT_ALT_NAME = '2.5.29.17';
const EXTENSION_OID_BASIC_CONSTRAINTS = '2.5.29.19';
const EXTENSION_OID_AUTHORITY_KEY_ID = '2.5.29.35';
exports.EXTENSION_OID_SCT = '1.3.6.1.4.1.11129.2.4.2';
class X509Certificate {
    constructor(asn1) {
        this.root = asn1;
    }
    static parse(cert) {
        const der = typeof cert === 'string' ? pem.toDER(cert) : cert;
        const asn1 = asn1_1.ASN1Obj.parseBuffer(der);
        return new X509Certificate(asn1);
    }
    get tbsCertificate() {
        return this.tbsCertificateObj;
    }
    get version() {
        // version number is the first element of the version context specific tag
        const ver = this.versionObj.subs[0].toInteger();
        return `v${(ver + BigInt(1)).toString()}`;
    }
    get serialNumber() {
        return this.serialNumberObj.value;
    }
    get notBefore() {
        // notBefore is the first element of the validity sequence
        return this.validityObj.subs[0].toDate();
    }
    get notAfter() {
        // notAfter is the second element of the validity sequence
        return this.validityObj.subs[1].toDate();
    }
    get issuer() {
        return this.issuerObj.value;
    }
    get subject() {
        return this.subjectObj.value;
    }
    get publicKey() {
        return this.subjectPublicKeyInfoObj.toDER();
    }
    get signatureAlgorithm() {
        const oid = this.signatureAlgorithmObj.subs[0].toOID();
        return oid_1.ECDSA_SIGNATURE_ALGOS[oid];
    }
    get signatureValue() {
        // Signature value is a bit string, so we need to skip the first byte
        return this.signatureValueObj.value.subarray(1);
    }
    get subjectAltName() {
        const ext = this.extSubjectAltName;
        return ext?.uri || ext?.rfc822Name;
    }
    get extensions() {
        // The extension list is the first (and only) element of the extensions
        // context specific tag
        const extSeq = this.extensionsObj?.subs[0];
        return extSeq?.subs || /* istanbul ignore next */ [];
    }
    get extKeyUsage() {
        const ext = this.findExtension(EXTENSION_OID_KEY_USAGE);
        return ext ? new ext_1.X509KeyUsageExtension(ext) : undefined;
    }
    get extBasicConstraints() {
        const ext = this.findExtension(EXTENSION_OID_BASIC_CONSTRAINTS);
        return ext ? new ext_1.X509BasicConstraintsExtension(ext) : undefined;
    }
    get extSubjectAltName() {
        const ext = this.findExtension(EXTENSION_OID_SUBJECT_ALT_NAME);
        return ext ? new ext_1.X509SubjectAlternativeNameExtension(ext) : undefined;
    }
    get extAuthorityKeyID() {
        const ext = this.findExtension(EXTENSION_OID_AUTHORITY_KEY_ID);
        return ext ? new ext_1.X509AuthorityKeyIDExtension(ext) : undefined;
    }
    get extSubjectKeyID() {
        const ext = this.findExtension(EXTENSION_OID_SUBJECT_KEY_ID);
        return ext
            ? new ext_1.X509SubjectKeyIDExtension(ext)
            : /* istanbul ignore next */ undefined;
    }
    get extSCT() {
        const ext = this.findExtension(exports.EXTENSION_OID_SCT);
        return ext ? new ext_1.X509SCTExtension(ext) : undefined;
    }
    get isCA() {
        const ca = this.extBasicConstraints?.isCA || false;
        // If the KeyUsage extension is present, keyCertSign must be set
        if (this.extKeyUsage) {
            ca && this.extKeyUsage.keyCertSign;
        }
        return ca;
    }
    extension(oid) {
        const ext = this.findExtension(oid);
        return ext ? new ext_1.X509Extension(ext) : undefined;
    }
    verify(issuerCertificate) {
        // Use the issuer's public key if provided, otherwise use the subject's
        const publicKey = issuerCertificate?.publicKey || this.publicKey;
        const key = crypto.createPublicKey(publicKey);
        return crypto.verify(this.tbsCertificate.toDER(), key, this.signatureValue, this.signatureAlgorithm);
    }
    validForDate(date) {
        return this.notBefore <= date && date <= this.notAfter;
    }
    equals(other) {
        return this.root.toDER().equals(other.root.toDER());
    }
    // Creates a copy of the certificate with a new buffer
    clone() {
        const der = this.root.toDER();
        const clone = Buffer.alloc(der.length);
        der.copy(clone);
        return X509Certificate.parse(clone);
    }
    findExtension(oid) {
        // Find the extension with the given OID. The OID will always be the first
        // element of the extension sequence
        return this.extensions.find((ext) => ext.subs[0].toOID() === oid);
    }
    /////////////////////////////////////////////////////////////////////////////
    // The following properties use the documented x509 structure to locate the
    // desired ASN.1 object
    // https://www.rfc-editor.org/rfc/rfc5280#section-4.1
    // https://www.rfc-editor.org/rfc/rfc5280#section-4.1.1.1
    get tbsCertificateObj() {
        // tbsCertificate is the first element of the certificate sequence
        return this.root.subs[0];
    }
    // https://www.rfc-editor.org/rfc/rfc5280#section-4.1.1.2
    get signatureAlgorithmObj() {
        // signatureAlgorithm is the second element of the certificate sequence
        return this.root.subs[1];
    }
    // https://www.rfc-editor.org/rfc/rfc5280#section-4.1.1.3
    get signatureValueObj() {
        // signatureValue is the third element of the certificate sequence
        return this.root.subs[2];
    }
    // https://www.rfc-editor.org/rfc/rfc5280#section-4.1.2.1
    get versionObj() {
        // version is the first element of the tbsCertificate sequence
        return this.tbsCertificateObj.subs[0];
    }
    // https://www.rfc-editor.org/rfc/rfc5280#section-4.1.2.2
    get serialNumberObj() {
        // serialNumber is the second element of the tbsCertificate sequence
        return this.tbsCertificateObj.subs[1];
    }
    // https://www.rfc-editor.org/rfc/rfc5280#section-4.1.2.4
    get issuerObj() {
        // issuer is the fourth element of the tbsCertificate sequence
        return this.tbsCertificateObj.subs[3];
    }
    // https://www.rfc-editor.org/rfc/rfc5280#section-4.1.2.5
    get validityObj() {
        // version is the fifth element of the tbsCertificate sequence
        return this.tbsCertificateObj.subs[4];
    }
    // https://www.rfc-editor.org/rfc/rfc5280#section-4.1.2.6
    get subjectObj() {
        // subject is the sixth element of the tbsCertificate sequence
        return this.tbsCertificateObj.subs[5];
    }
    // https://www.rfc-editor.org/rfc/rfc5280#section-4.1.2.7
    get subjectPublicKeyInfoObj() {
        // subjectPublicKeyInfo is the seventh element of the tbsCertificate sequence
        return this.tbsCertificateObj.subs[6];
    }
    // Extensions can't be located by index because their position varies. Instead,
    // we need to find the extensions context specific tag
    // https://www.rfc-editor.org/rfc/rfc5280#section-4.1.2.9
    get extensionsObj() {
        return this.tbsCertificateObj.subs.find((sub) => sub.tag.isContextSpecific(0x03));
    }
}
exports.X509Certificate = X509Certificate;