Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/jdk17u
Path: blob/master/test/jdk/java/security/testlibrary/CertificateBuilder.java
66644 views
1
/*
2
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package sun.security.testlibrary;
27
28
import java.io.*;
29
import java.util.*;
30
import java.security.*;
31
import java.security.cert.X509Certificate;
32
import java.security.cert.CertificateException;
33
import java.security.cert.CertificateFactory;
34
import java.security.cert.Extension;
35
import javax.security.auth.x500.X500Principal;
36
import java.math.BigInteger;
37
38
import sun.security.util.DerOutputStream;
39
import sun.security.util.DerValue;
40
import sun.security.util.ObjectIdentifier;
41
import sun.security.util.SignatureUtil;
42
import sun.security.x509.AccessDescription;
43
import sun.security.x509.AlgorithmId;
44
import sun.security.x509.AuthorityInfoAccessExtension;
45
import sun.security.x509.AuthorityKeyIdentifierExtension;
46
import sun.security.x509.SubjectKeyIdentifierExtension;
47
import sun.security.x509.BasicConstraintsExtension;
48
import sun.security.x509.ExtendedKeyUsageExtension;
49
import sun.security.x509.DNSName;
50
import sun.security.x509.GeneralName;
51
import sun.security.x509.GeneralNames;
52
import sun.security.x509.KeyUsageExtension;
53
import sun.security.x509.SerialNumber;
54
import sun.security.x509.SubjectAlternativeNameExtension;
55
import sun.security.x509.URIName;
56
import sun.security.x509.KeyIdentifier;
57
58
/**
59
* Helper class that builds and signs X.509 certificates.
60
*
61
* A CertificateBuilder is created with a default constructor, and then
62
* uses additional public methods to set the public key, desired validity
63
* dates, serial number and extensions. It is expected that the caller will
64
* have generated the necessary key pairs prior to using a CertificateBuilder
65
* to generate certificates.
66
*
67
* The following methods are mandatory before calling build():
68
* <UL>
69
* <LI>{@link #setSubjectName(java.lang.String)}
70
* <LI>{@link #setPublicKey(java.security.PublicKey)}
71
* <LI>{@link #setNotBefore(java.util.Date)} and
72
* {@link #setNotAfter(java.util.Date)}, or
73
* {@link #setValidity(java.util.Date, java.util.Date)}
74
* <LI>{@link #setSerialNumber(java.math.BigInteger)}
75
* </UL><BR>
76
*
77
* Additionally, the caller can either provide a {@link List} of
78
* {@link Extension} objects, or use the helper classes to add specific
79
* extension types.
80
*
81
* When all required and desired parameters are set, the
82
* {@link #build(java.security.cert.X509Certificate, java.security.PrivateKey,
83
* java.lang.String)} method can be used to create the {@link X509Certificate}
84
* object.
85
*
86
* Multiple certificates may be cut from the same settings using subsequent
87
* calls to the build method. Settings may be cleared using the
88
* {@link #reset()} method.
89
*/
90
public class CertificateBuilder {
91
private final CertificateFactory factory;
92
93
private X500Principal subjectName = null;
94
private BigInteger serialNumber = null;
95
private PublicKey publicKey = null;
96
private Date notBefore = null;
97
private Date notAfter = null;
98
private final Map<String, Extension> extensions = new HashMap<>();
99
private byte[] tbsCertBytes;
100
private byte[] signatureBytes;
101
102
/**
103
* Default constructor for a {@code CertificateBuilder} object.
104
*
105
* @throws CertificateException if the underlying {@link CertificateFactory}
106
* cannot be instantiated.
107
*/
108
public CertificateBuilder() throws CertificateException {
109
factory = CertificateFactory.getInstance("X.509");
110
}
111
112
/**
113
* Set the subject name for the certificate.
114
*
115
* @param name An {@link X500Principal} to be used as the subject name
116
* on this certificate.
117
*/
118
public void setSubjectName(X500Principal name) {
119
subjectName = name;
120
}
121
122
/**
123
* Set the subject name for the certificate.
124
*
125
* @param name The subject name in RFC 2253 format
126
*/
127
public void setSubjectName(String name) {
128
subjectName = new X500Principal(name);
129
}
130
131
/**
132
* Set the public key for this certificate.
133
*
134
* @param pubKey The {@link PublicKey} to be used on this certificate.
135
*/
136
public void setPublicKey(PublicKey pubKey) {
137
publicKey = Objects.requireNonNull(pubKey, "Caught null public key");
138
}
139
140
/**
141
* Set the NotBefore date on the certificate.
142
*
143
* @param nbDate A {@link Date} object specifying the start of the
144
* certificate validity period.
145
*/
146
public void setNotBefore(Date nbDate) {
147
Objects.requireNonNull(nbDate, "Caught null notBefore date");
148
notBefore = (Date)nbDate.clone();
149
}
150
151
/**
152
* Set the NotAfter date on the certificate.
153
*
154
* @param naDate A {@link Date} object specifying the end of the
155
* certificate validity period.
156
*/
157
public void setNotAfter(Date naDate) {
158
Objects.requireNonNull(naDate, "Caught null notAfter date");
159
notAfter = (Date)naDate.clone();
160
}
161
162
/**
163
* Set the validity period for the certificate
164
*
165
* @param nbDate A {@link Date} object specifying the start of the
166
* certificate validity period.
167
* @param naDate A {@link Date} object specifying the end of the
168
* certificate validity period.
169
*/
170
public void setValidity(Date nbDate, Date naDate) {
171
setNotBefore(nbDate);
172
setNotAfter(naDate);
173
}
174
175
/**
176
* Set the serial number on the certificate.
177
*
178
* @param serial A serial number in {@link BigInteger} form.
179
*/
180
public void setSerialNumber(BigInteger serial) {
181
Objects.requireNonNull(serial, "Caught null serial number");
182
serialNumber = serial;
183
}
184
185
186
/**
187
* Add a single extension to the certificate.
188
*
189
* @param ext The extension to be added.
190
*/
191
public void addExtension(Extension ext) {
192
Objects.requireNonNull(ext, "Caught null extension");
193
extensions.put(ext.getId(), ext);
194
}
195
196
/**
197
* Add multiple extensions contained in a {@code List}.
198
*
199
* @param extList The {@link List} of extensions to be added to
200
* the certificate.
201
*/
202
public void addExtensions(List<Extension> extList) {
203
Objects.requireNonNull(extList, "Caught null extension list");
204
for (Extension ext : extList) {
205
extensions.put(ext.getId(), ext);
206
}
207
}
208
209
/**
210
* Helper method to add DNSName types for the SAN extension
211
*
212
* @param dnsNames A {@code List} of names to add as DNSName types
213
*
214
* @throws IOException if an encoding error occurs.
215
*/
216
public void addSubjectAltNameDNSExt(List<String> dnsNames) throws IOException {
217
if (!dnsNames.isEmpty()) {
218
GeneralNames gNames = new GeneralNames();
219
for (String name : dnsNames) {
220
gNames.add(new GeneralName(new DNSName(name)));
221
}
222
addExtension(new SubjectAlternativeNameExtension(false,
223
gNames));
224
}
225
}
226
227
/**
228
* Helper method to add one or more OCSP URIs to the Authority Info Access
229
* certificate extension.
230
*
231
* @param locations A list of one or more OCSP responder URIs as strings
232
*
233
* @throws IOException if an encoding error occurs.
234
*/
235
public void addAIAExt(List<String> locations)
236
throws IOException {
237
if (!locations.isEmpty()) {
238
List<AccessDescription> acDescList = new ArrayList<>();
239
for (String ocspUri : locations) {
240
acDescList.add(new AccessDescription(
241
AccessDescription.Ad_OCSP_Id,
242
new GeneralName(new URIName(ocspUri))));
243
}
244
addExtension(new AuthorityInfoAccessExtension(acDescList));
245
}
246
}
247
248
/**
249
* Set a Key Usage extension for the certificate. The extension will
250
* be marked critical.
251
*
252
* @param bitSettings Boolean array for all nine bit settings in the order
253
* documented in RFC 5280 section 4.2.1.3.
254
*
255
* @throws IOException if an encoding error occurs.
256
*/
257
public void addKeyUsageExt(boolean[] bitSettings) throws IOException {
258
addExtension(new KeyUsageExtension(bitSettings));
259
}
260
261
/**
262
* Set the Basic Constraints Extension for a certificate.
263
*
264
* @param crit {@code true} if critical, {@code false} otherwise
265
* @param isCA {@code true} if the extension will be on a CA certificate,
266
* {@code false} otherwise
267
* @param maxPathLen The maximum path length issued by this CA. Values
268
* less than zero will omit this field from the resulting extension and
269
* no path length constraint will be asserted.
270
*
271
* @throws IOException if an encoding error occurs.
272
*/
273
public void addBasicConstraintsExt(boolean crit, boolean isCA,
274
int maxPathLen) throws IOException {
275
addExtension(new BasicConstraintsExtension(crit, isCA, maxPathLen));
276
}
277
278
/**
279
* Add the Authority Key Identifier extension.
280
*
281
* @param authorityCert The certificate of the issuing authority.
282
*
283
* @throws IOException if an encoding error occurs.
284
*/
285
public void addAuthorityKeyIdExt(X509Certificate authorityCert)
286
throws IOException {
287
addAuthorityKeyIdExt(authorityCert.getPublicKey());
288
}
289
290
/**
291
* Add the Authority Key Identifier extension.
292
*
293
* @param authorityKey The public key of the issuing authority.
294
*
295
* @throws IOException if an encoding error occurs.
296
*/
297
public void addAuthorityKeyIdExt(PublicKey authorityKey) throws IOException {
298
KeyIdentifier kid = new KeyIdentifier(authorityKey);
299
addExtension(new AuthorityKeyIdentifierExtension(kid, null, null));
300
}
301
302
/**
303
* Add the Subject Key Identifier extension.
304
*
305
* @param subjectKey The public key to be used in the resulting certificate
306
*
307
* @throws IOException if an encoding error occurs.
308
*/
309
public void addSubjectKeyIdExt(PublicKey subjectKey) throws IOException {
310
byte[] keyIdBytes = new KeyIdentifier(subjectKey).getIdentifier();
311
addExtension(new SubjectKeyIdentifierExtension(keyIdBytes));
312
}
313
314
/**
315
* Add the Extended Key Usage extension.
316
*
317
* @param ekuOids A {@link List} of object identifiers in string form.
318
*
319
* @throws IOException if an encoding error occurs.
320
*/
321
public void addExtendedKeyUsageExt(List<String> ekuOids)
322
throws IOException {
323
if (!ekuOids.isEmpty()) {
324
Vector<ObjectIdentifier> oidVector = new Vector<>();
325
for (String oid : ekuOids) {
326
oidVector.add(ObjectIdentifier.of(oid));
327
}
328
addExtension(new ExtendedKeyUsageExtension(oidVector));
329
}
330
}
331
332
/**
333
* Clear all settings and return the {@code CertificateBuilder} to
334
* its default state.
335
*/
336
public void reset() {
337
extensions.clear();
338
subjectName = null;
339
notBefore = null;
340
notAfter = null;
341
serialNumber = null;
342
publicKey = null;
343
signatureBytes = null;
344
tbsCertBytes = null;
345
}
346
347
/**
348
* Build the certificate.
349
*
350
* @param issuerCert The certificate of the issuing authority, or
351
* {@code null} if the resulting certificate is self-signed.
352
* @param issuerKey The private key of the issuing authority
353
* @param algName The signature algorithm name
354
*
355
* @return The resulting {@link X509Certificate}
356
*
357
* @throws IOException if an encoding error occurs.
358
* @throws CertificateException If the certificate cannot be generated
359
* by the underlying {@link CertificateFactory}
360
* @throws NoSuchAlgorithmException If an invalid signature algorithm
361
* is provided.
362
*/
363
public X509Certificate build(X509Certificate issuerCert,
364
PrivateKey issuerKey, String algName)
365
throws IOException, CertificateException, NoSuchAlgorithmException {
366
// TODO: add some basic checks (key usage, basic constraints maybe)
367
368
byte[] encodedCert = encodeTopLevel(issuerCert, issuerKey, algName);
369
ByteArrayInputStream bais = new ByteArrayInputStream(encodedCert);
370
return (X509Certificate)factory.generateCertificate(bais);
371
}
372
373
/**
374
* Encode the contents of the outer-most ASN.1 SEQUENCE:
375
*
376
* <PRE>
377
* Certificate ::= SEQUENCE {
378
* tbsCertificate TBSCertificate,
379
* signatureAlgorithm AlgorithmIdentifier,
380
* signatureValue BIT STRING }
381
* </PRE>
382
*
383
* @param issuerCert The certificate of the issuing authority, or
384
* {@code null} if the resulting certificate is self-signed.
385
* @param issuerKey The private key of the issuing authority
386
* @param signAlg The signature algorithm object
387
*
388
* @return The DER-encoded X.509 certificate
389
*
390
* @throws CertificateException If an error occurs during the
391
* signing process.
392
* @throws IOException if an encoding error occurs.
393
*/
394
private byte[] encodeTopLevel(X509Certificate issuerCert,
395
PrivateKey issuerKey, String algName)
396
throws CertificateException, IOException, NoSuchAlgorithmException {
397
398
AlgorithmId signAlg = AlgorithmId.get(algName);
399
DerOutputStream outerSeq = new DerOutputStream();
400
DerOutputStream topLevelItems = new DerOutputStream();
401
402
try {
403
Signature sig = SignatureUtil.fromKey(signAlg.getName(), issuerKey, (Provider)null);
404
// Rewrite signAlg, RSASSA-PSS needs some parameters.
405
signAlg = SignatureUtil.fromSignature(sig, issuerKey);
406
tbsCertBytes = encodeTbsCert(issuerCert, signAlg);
407
sig.update(tbsCertBytes);
408
signatureBytes = sig.sign();
409
} catch (GeneralSecurityException ge) {
410
throw new CertificateException(ge);
411
}
412
topLevelItems.write(tbsCertBytes);
413
signAlg.derEncode(topLevelItems);
414
topLevelItems.putBitString(signatureBytes);
415
outerSeq.write(DerValue.tag_Sequence, topLevelItems);
416
417
return outerSeq.toByteArray();
418
}
419
420
/**
421
* Encode the bytes for the TBSCertificate structure:
422
* <PRE>
423
* TBSCertificate ::= SEQUENCE {
424
* version [0] EXPLICIT Version DEFAULT v1,
425
* serialNumber CertificateSerialNumber,
426
* signature AlgorithmIdentifier,
427
* issuer Name,
428
* validity Validity,
429
* subject Name,
430
* subjectPublicKeyInfo SubjectPublicKeyInfo,
431
* issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
432
* -- If present, version MUST be v2 or v3
433
* subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
434
* -- If present, version MUST be v2 or v3
435
* extensions [3] EXPLICIT Extensions OPTIONAL
436
* -- If present, version MUST be v3
437
* }
438
*
439
* @param issuerCert The certificate of the issuing authority, or
440
* {@code null} if the resulting certificate is self-signed.
441
* @param signAlg The signature algorithm object
442
*
443
* @return The DER-encoded bytes for the TBSCertificate structure
444
*
445
* @throws IOException if an encoding error occurs.
446
*/
447
private byte[] encodeTbsCert(X509Certificate issuerCert,
448
AlgorithmId signAlg) throws IOException {
449
DerOutputStream tbsCertSeq = new DerOutputStream();
450
DerOutputStream tbsCertItems = new DerOutputStream();
451
452
// Hardcode to V3
453
byte[] v3int = {0x02, 0x01, 0x02};
454
tbsCertItems.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,
455
(byte)0), v3int);
456
457
// Serial Number
458
SerialNumber sn = new SerialNumber(serialNumber);
459
sn.encode(tbsCertItems);
460
461
// Algorithm ID
462
signAlg.derEncode(tbsCertItems);
463
464
// Issuer Name
465
if (issuerCert != null) {
466
tbsCertItems.write(
467
issuerCert.getSubjectX500Principal().getEncoded());
468
} else {
469
// Self-signed
470
tbsCertItems.write(subjectName.getEncoded());
471
}
472
473
// Validity period (set as UTCTime)
474
DerOutputStream valSeq = new DerOutputStream();
475
valSeq.putUTCTime(notBefore);
476
valSeq.putUTCTime(notAfter);
477
tbsCertItems.write(DerValue.tag_Sequence, valSeq);
478
479
// Subject Name
480
tbsCertItems.write(subjectName.getEncoded());
481
482
// SubjectPublicKeyInfo
483
tbsCertItems.write(publicKey.getEncoded());
484
485
// TODO: Extensions!
486
encodeExtensions(tbsCertItems);
487
488
// Wrap it all up in a SEQUENCE and return the bytes
489
tbsCertSeq.write(DerValue.tag_Sequence, tbsCertItems);
490
return tbsCertSeq.toByteArray();
491
}
492
493
/**
494
* Encode the extensions segment for an X.509 Certificate:
495
*
496
* <PRE>
497
* Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
498
*
499
* Extension ::= SEQUENCE {
500
* extnID OBJECT IDENTIFIER,
501
* critical BOOLEAN DEFAULT FALSE,
502
* extnValue OCTET STRING
503
* -- contains the DER encoding of an ASN.1 value
504
* -- corresponding to the extension type identified
505
* -- by extnID
506
* }
507
* </PRE>
508
*
509
* @param tbsStream The {@code DerOutputStream} that holds the
510
* TBSCertificate contents.
511
*
512
* @throws IOException if an encoding error occurs.
513
*/
514
private void encodeExtensions(DerOutputStream tbsStream)
515
throws IOException {
516
DerOutputStream extSequence = new DerOutputStream();
517
DerOutputStream extItems = new DerOutputStream();
518
519
for (Extension ext : extensions.values()) {
520
ext.encode(extItems);
521
}
522
extSequence.write(DerValue.tag_Sequence, extItems);
523
tbsStream.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,
524
(byte)3), extSequence);
525
}
526
527
}
528
529