1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.pkitesting;
17
18 import io.netty.util.internal.EmptyArrays;
19 import org.bouncycastle.asn1.ASN1Integer;
20 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
21 import org.bouncycastle.asn1.DERBitString;
22 import org.bouncycastle.asn1.DERIA5String;
23 import org.bouncycastle.asn1.DERUTF8String;
24 import org.bouncycastle.asn1.x500.X500Name;
25 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
26 import org.bouncycastle.asn1.x509.BasicConstraints;
27 import org.bouncycastle.asn1.x509.CRLDistPoint;
28 import org.bouncycastle.asn1.x509.DistributionPoint;
29 import org.bouncycastle.asn1.x509.DistributionPointName;
30 import org.bouncycastle.asn1.x509.Extension;
31 import org.bouncycastle.asn1.x509.ExtensionsGenerator;
32 import org.bouncycastle.asn1.x509.GeneralName;
33 import org.bouncycastle.asn1.x509.GeneralNames;
34 import org.bouncycastle.asn1.x509.KeyPurposeId;
35 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
36 import org.bouncycastle.asn1.x509.Time;
37 import org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
38 import org.bouncycastle.jcajce.spec.EdDSAParameterSpec;
39
40 import java.io.IOException;
41 import java.io.UncheckedIOException;
42 import java.lang.reflect.Method;
43 import java.math.BigInteger;
44 import java.net.InetAddress;
45 import java.net.URI;
46 import java.net.URISyntaxException;
47 import java.security.GeneralSecurityException;
48 import java.security.KeyPair;
49 import java.security.KeyPairGenerator;
50 import java.security.PrivateKey;
51 import java.security.PublicKey;
52 import java.security.SecureRandom;
53 import java.security.cert.CertificateFactory;
54 import java.security.cert.X509Certificate;
55 import java.security.interfaces.DSAPublicKey;
56 import java.security.interfaces.ECPublicKey;
57 import java.security.interfaces.RSAPublicKey;
58 import java.security.spec.AlgorithmParameterSpec;
59 import java.security.spec.ECGenParameterSpec;
60 import java.security.spec.RSAKeyGenParameterSpec;
61 import java.time.Instant;
62 import java.time.temporal.ChronoUnit;
63 import java.util.ArrayList;
64 import java.util.Date;
65 import java.util.List;
66 import java.util.Objects;
67 import java.util.OptionalInt;
68 import java.util.Set;
69 import java.util.TreeSet;
70 import javax.security.auth.x500.X500Principal;
71
72 import static java.util.Objects.requireNonNull;
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 public final class CertificateBuilder {
106
107 static final String OID_X509_NAME_CONSTRAINTS = "2.5.29.30";
108 static final String OID_PKIX_KP = "1.3.6.1.5.5.7.3";
109 static final String OID_PKIX_KP_SERVER_AUTH = OID_PKIX_KP + ".1";
110 static final String OID_PKIX_KP_CLIENT_AUTH = OID_PKIX_KP + ".2";
111 static final String OID_PKIX_KP_CODE_SIGNING = OID_PKIX_KP + ".3";
112 static final String OID_PKIX_KP_EMAIL_PROTECTION = OID_PKIX_KP + ".4";
113 static final String OID_PKIX_KP_TIME_STAMPING = OID_PKIX_KP + ".8";
114 static final String OID_PKIX_KP_OCSP_SIGNING = OID_PKIX_KP + ".9";
115 static final String OID_KERBEROS_KEY_PURPOSE_CLIENT_AUTH = "1.3.6.1.5.2.3.4";
116 static final String OID_MICROSOFT_SMARTCARD_LOGIN = "1.3.6.1.4.1.311.20.2.2";
117 private static final GeneralName[] EMPTY_GENERAL_NAMES = new GeneralName[0];
118 private static final DistributionPoint[] EMPTY_DIST_POINTS = new DistributionPoint[0];
119 private static final AlgorithmParameterSpec UNSUPPORTED_SPEC = new AlgorithmParameterSpec() {
120 };
121 private static final String UNSUPPORTED_SIGN = "UNSUPPORTED_SIGN";
122
123 SecureRandom random;
124 Algorithm algorithm = Algorithm.ecp256;
125 Instant notBefore = Instant.now().minus(1, ChronoUnit.DAYS);
126 Instant notAfter = Instant.now().plus(1, ChronoUnit.DAYS);
127 List<BuilderCallback> modifierCallbacks = new ArrayList<>();
128 List<GeneralName> subjectAlternativeNames = new ArrayList<>();
129 List<DistributionPoint> crlDistributionPoints = new ArrayList<>();
130 BigInteger serial;
131 X500Principal subject;
132 boolean isCertificateAuthority;
133 OptionalInt pathLengthConstraint = OptionalInt.empty();
134 PublicKey publicKey;
135 Set<String> extendedKeyUsage = new TreeSet<>();
136 Extension keyUsage;
137
138
139
140
141
142
143
144 public CertificateBuilder() {
145 }
146
147
148
149
150
151 public CertificateBuilder copy() {
152 CertificateBuilder copy = new CertificateBuilder();
153 copy.random = random;
154 copy.algorithm = algorithm;
155 copy.notBefore = notBefore;
156 copy.notAfter = notAfter;
157 copy.modifierCallbacks = new ArrayList<>(modifierCallbacks);
158 copy.subjectAlternativeNames = new ArrayList<>(subjectAlternativeNames);
159 copy.crlDistributionPoints = new ArrayList<>(crlDistributionPoints);
160 copy.serial = serial;
161 copy.subject = subject;
162 copy.isCertificateAuthority = isCertificateAuthority;
163 copy.pathLengthConstraint = pathLengthConstraint;
164 copy.publicKey = publicKey;
165 copy.keyUsage = keyUsage;
166 copy.extendedKeyUsage = new TreeSet<>(extendedKeyUsage);
167 return copy;
168 }
169
170
171
172
173
174
175 public CertificateBuilder secureRandom(SecureRandom secureRandom) {
176 random = requireNonNull(secureRandom);
177 return this;
178 }
179
180
181
182
183
184
185 public CertificateBuilder notBefore(Instant instant) {
186 notBefore = requireNonNull(instant);
187 return this;
188 }
189
190
191
192
193
194
195 public CertificateBuilder notAfter(Instant instant) {
196 notAfter = requireNonNull(instant);
197 return this;
198 }
199
200
201
202
203
204
205
206 public CertificateBuilder serial(BigInteger serial) {
207 this.serial = serial;
208 return this;
209 }
210
211
212
213
214
215
216 public CertificateBuilder subject(String fqdn) {
217 subject = new X500Principal(requireNonNull(fqdn));
218 return this;
219 }
220
221
222
223
224
225
226 public CertificateBuilder subject(X500Principal name) {
227 subject = requireNonNull(name);
228 return this;
229 }
230
231
232
233
234
235
236
237
238 public CertificateBuilder addSanOtherName(String typeOid, byte[] encodedValue) {
239 subjectAlternativeNames.add(GeneralNameUtils.otherName(typeOid, encodedValue));
240 return this;
241 }
242
243
244
245
246
247
248
249 public CertificateBuilder addSanRfc822Name(String name) {
250 subjectAlternativeNames.add(GeneralNameUtils.rfc822Name(name));
251 return this;
252 }
253
254
255
256
257
258
259 public CertificateBuilder addSanDnsName(String dns) {
260 if (dns.trim().isEmpty()) {
261 throw new IllegalArgumentException("Blank DNS SANs are forbidden by RFC 5280, Section 4.2.1.6.");
262 }
263 subjectAlternativeNames.add(GeneralNameUtils.dnsName(dns));
264 return this;
265 }
266
267
268
269
270
271
272
273
274
275 public CertificateBuilder addSanDirectoryName(String dirName) {
276 subjectAlternativeNames.add(GeneralNameUtils.directoryName(dirName));
277 return this;
278 }
279
280
281
282
283
284
285
286
287 public CertificateBuilder addSanUriName(String uri) throws URISyntaxException {
288 subjectAlternativeNames.add(GeneralNameUtils.uriName(uri));
289 return this;
290 }
291
292
293
294
295
296
297 public CertificateBuilder addSanUriName(URI uri) {
298 subjectAlternativeNames.add(GeneralNameUtils.uriName(uri));
299 return this;
300 }
301
302
303
304
305
306
307
308 public CertificateBuilder addSanIpAddress(String ipAddress) {
309 subjectAlternativeNames.add(GeneralNameUtils.ipAddress(ipAddress));
310 return this;
311 }
312
313
314
315
316
317
318
319 public CertificateBuilder addSanIpAddress(InetAddress ipAddress) {
320 subjectAlternativeNames.add(GeneralNameUtils.ipAddress(ipAddress.getHostAddress()));
321 return this;
322 }
323
324
325
326
327
328
329
330 public CertificateBuilder addSanRegisteredId(String oid) {
331 subjectAlternativeNames.add(GeneralNameUtils.registeredId(oid));
332 return this;
333 }
334
335
336
337
338
339
340
341
342
343
344
345 public CertificateBuilder addCrlDistributionPoint(URI uri) {
346 GeneralName fullName = GeneralNameUtils.uriName(uri);
347 crlDistributionPoints.add(new DistributionPoint(
348 new DistributionPointName(new GeneralNames(fullName)),
349 null,
350 null));
351 return this;
352 }
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368 public CertificateBuilder addCrlDistributionPoint(URI uri, X500Principal issuer) {
369 GeneralName fullName = GeneralNameUtils.uriName(uri);
370 GeneralName issuerName = GeneralNameUtils.directoryName(issuer);
371 crlDistributionPoints.add(new DistributionPoint(
372 new DistributionPointName(new GeneralNames(fullName)),
373 null,
374 new GeneralNames(issuerName)));
375 return this;
376 }
377
378
379
380
381
382
383
384
385 public CertificateBuilder setIsCertificateAuthority(boolean isCA) {
386 isCertificateAuthority = isCA;
387 return this;
388 }
389
390
391
392
393
394
395 public CertificateBuilder setPathLengthConstraint(OptionalInt pathLengthConstraint) {
396 this.pathLengthConstraint = requireNonNull(pathLengthConstraint, "pathLengthConstraint");
397 return this;
398 }
399
400
401
402
403
404
405 public CertificateBuilder algorithm(Algorithm algorithm) {
406 requireNonNull(algorithm, "algorithm");
407 if (algorithm.parameterSpec == UNSUPPORTED_SPEC) {
408 throw new UnsupportedOperationException("This algorithm is not supported: " + algorithm);
409 }
410 this.algorithm = algorithm;
411 return this;
412 }
413
414
415
416
417
418
419
420 public CertificateBuilder ecp256() {
421 return algorithm(Algorithm.ecp256);
422 }
423
424
425
426
427
428
429
430 public CertificateBuilder rsa2048() {
431 return algorithm(Algorithm.rsa2048);
432 }
433
434
435
436
437
438
439
440
441
442
443
444
445
446 public CertificateBuilder publicKey(PublicKey key) {
447 publicKey = key;
448 return this;
449 }
450
451 private CertificateBuilder addExtension(String identifierOid, boolean critical, byte[] value) {
452 requireNonNull(identifierOid, "identifierOid");
453 requireNonNull(value, "value");
454 modifierCallbacks.add(builder -> {
455 builder.addExtension(new Extension(
456 new ASN1ObjectIdentifier(identifierOid),
457 critical,
458 value));
459 });
460 return this;
461 }
462
463
464
465
466
467
468
469
470
471 public CertificateBuilder addExtensionOctetString(String identifierOID, boolean critical, byte[] contents) {
472 requireNonNull(identifierOID, "identifierOID");
473 requireNonNull(contents, "contents");
474 modifierCallbacks.add(builder -> {
475 builder.addExtension(new Extension(
476 new ASN1ObjectIdentifier(identifierOID),
477 critical,
478 contents));
479 });
480 return this;
481 }
482
483
484
485
486
487
488
489
490
491
492
493 public CertificateBuilder addExtensionUtf8String(String identifierOID, boolean critical, String value) {
494 try {
495 return addExtension(identifierOID, critical, new DERUTF8String(value).getEncoded("DER"));
496 } catch (IOException e) {
497 throw new UncheckedIOException(e);
498 }
499 }
500
501
502
503
504
505
506
507
508
509
510
511 public CertificateBuilder addExtensionAsciiString(String identifierOID, boolean critical, String value) {
512 try {
513 return addExtension(identifierOID, critical, new DERIA5String(value).getEncoded("DER"));
514 } catch (IOException e) {
515 throw new UncheckedIOException(e);
516 }
517 }
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534 public CertificateBuilder setKeyUsage(boolean critical, KeyUsage... keyUsages) {
535 int maxBit = 0;
536 for (KeyUsage usage : keyUsages) {
537 maxBit = Math.max(usage.bitId, maxBit);
538 }
539 boolean[] bits = new boolean[maxBit + 1];
540 for (KeyUsage usage : keyUsages) {
541 bits[usage.bitId] = true;
542 }
543 int padding = 8 - bits.length % 8;
544 int lenBytes = bits.length / 8 + 1;
545 if (padding == 8) {
546 padding = 0;
547 lenBytes--;
548 }
549 byte[] bytes = new byte[lenBytes];
550 for (int i = 0; i < bits.length; i++) {
551 if (bits[i]) {
552 int byteIndex = i / 8;
553 int bitIndex = i % 8;
554 bytes[byteIndex] |= (byte) (0x80 >>> bitIndex);
555 }
556 }
557
558 try {
559 keyUsage = new Extension(
560 Extension.keyUsage,
561 critical,
562 new DERBitString(bytes, padding).getEncoded("DER"));
563 } catch (IOException e) {
564 throw new UncheckedIOException(e);
565 }
566 return this;
567 }
568
569
570
571
572
573
574
575
576 public CertificateBuilder addExtendedKeyUsage(String oid) {
577 extendedKeyUsage.add(oid);
578 return this;
579 }
580
581
582
583
584
585
586 public CertificateBuilder addExtendedKeyUsage(ExtendedKeyUsage keyUsage) {
587 extendedKeyUsage.add(keyUsage.getOid());
588 return this;
589 }
590
591
592
593
594
595 public CertificateBuilder addExtendedKeyUsageServerAuth() {
596 return addExtendedKeyUsage(ExtendedKeyUsage.PKIX_KP_SERVER_AUTH);
597 }
598
599
600
601
602
603 public CertificateBuilder addExtendedKeyUsageClientAuth() {
604 return addExtendedKeyUsage(ExtendedKeyUsage.PKIX_KP_CLIENT_AUTH);
605 }
606
607
608
609
610
611 public CertificateBuilder addExtendedKeyUsageCodeSigning() {
612 return addExtendedKeyUsage(ExtendedKeyUsage.PKIX_KP_CODE_SIGNING);
613 }
614
615
616
617
618
619 public CertificateBuilder addExtendedKeyUsageEmailProtection() {
620 return addExtendedKeyUsage(ExtendedKeyUsage.PKIX_KP_EMAIL_PROTECTION);
621 }
622
623
624
625
626
627 public CertificateBuilder addExtendedKeyUsageTimeStamping() {
628 return addExtendedKeyUsage(ExtendedKeyUsage.PKIX_KP_TIME_STAMPING);
629 }
630
631
632
633
634
635 public CertificateBuilder addExtendedKeyUsageOcspSigning() {
636 return addExtendedKeyUsage(ExtendedKeyUsage.PKIX_KP_OCSP_SIGNING);
637 }
638
639
640
641
642
643 public CertificateBuilder addExtendedKeyUsageKerberosClientAuth() {
644 return addExtendedKeyUsage(ExtendedKeyUsage.KERBEROS_KEY_PURPOSE_CLIENT_AUTH);
645 }
646
647
648
649
650
651 public CertificateBuilder addExtendedKeyUsageMicrosoftSmartcardLogin() {
652 return addExtendedKeyUsage(ExtendedKeyUsage.MICROSOFT_SMARTCARD_LOGIN);
653 }
654
655
656
657
658
659
660 public X509Bundle buildSelfSigned() throws Exception {
661 if (publicKey != null) {
662 throw new IllegalStateException("Cannot create a self-signed certificate with a public key from a CSR.");
663 }
664 if (!algorithm.supportSigning()) {
665 throw new IllegalStateException("Cannot create a self-signed certificate with a " +
666 "key algorithm that does not support signing: " + algorithm);
667 }
668 KeyPair keyPair = generateKeyPair();
669
670 V3TBSCertificateGenerator generator = createCertBuilder(subject, subject, keyPair, algorithm.signatureType);
671
672 addExtensions(generator);
673
674 Signed signed = new Signed(tbsCertToBytes(generator), algorithm.signatureType, keyPair.getPrivate());
675 CertificateFactory factory = CertificateFactory.getInstance("X.509");
676 X509Certificate cert = (X509Certificate) factory.generateCertificate(signed.toInputStream());
677 return X509Bundle.fromRootCertificateAuthority(cert, keyPair);
678 }
679
680
681
682
683
684
685
686 public X509Bundle buildIssuedBy(X509Bundle issuerBundle) throws Exception {
687 String issuerSignAlgorithm = preferredSignatureAlgorithm(issuerBundle.getCertificate().getPublicKey());
688 return buildIssuedBy(issuerBundle, issuerSignAlgorithm);
689 }
690
691
692
693
694
695
696
697 public X509Bundle buildIssuedBy(X509Bundle issuerBundle, String signAlg) throws Exception {
698 final KeyPair keyPair;
699 if (publicKey == null) {
700 keyPair = generateKeyPair();
701 } else {
702 keyPair = new KeyPair(publicKey, null);
703 }
704
705 X500Principal issuerPrincipal = issuerBundle.getCertificate().getSubjectX500Principal();
706 V3TBSCertificateGenerator generator = createCertBuilder(issuerPrincipal, subject, keyPair, signAlg);
707
708 addExtensions(generator);
709
710 PrivateKey issuerPrivateKey = issuerBundle.getKeyPair().getPrivate();
711 if (issuerPrivateKey == null) {
712 throw new IllegalArgumentException(
713 "Cannot sign certificate with issuer bundle that does not have a private key.");
714 }
715 Signed signed = new Signed(tbsCertToBytes(generator), signAlg, issuerPrivateKey);
716 CertificateFactory factory = CertificateFactory.getInstance("X.509");
717 X509Certificate cert = (X509Certificate) factory.generateCertificate(signed.toInputStream());
718 X509Certificate[] issuerPath = issuerBundle.getCertificatePath();
719 X509Certificate[] path = new X509Certificate[issuerPath.length + 1];
720 path[0] = cert;
721 System.arraycopy(issuerPath, 0, path, 1, issuerPath.length);
722 return X509Bundle.fromCertificatePath(path, issuerBundle.getRootCertificate(), keyPair);
723 }
724
725 private static String preferredSignatureAlgorithm(PublicKey key) {
726 if (key instanceof RSAPublicKey) {
727 RSAPublicKey rsa = (RSAPublicKey) key;
728 if (rsa.getModulus().bitLength() < 4096) {
729 return "SHA256withRSA";
730 }
731 return "SHA384withRSA";
732 }
733 if (key instanceof ECPublicKey) {
734 ECPublicKey ec = (ECPublicKey) key;
735 int size = ec.getW().getAffineX().bitLength();
736
737 if (size <= 256) {
738 return "SHA256withECDSA";
739 }
740 if (size <= 384) {
741 return "SHA384withECDSA";
742 }
743 return "SHA512withECDSA";
744 }
745 if (key instanceof DSAPublicKey) {
746 throw new IllegalArgumentException("DSA keys are not supported because they are obsolete");
747 }
748 String keyAlgorithm = key.getAlgorithm();
749 if ("Ed25519".equals(keyAlgorithm) || "1.3.101.112".equals(keyAlgorithm)) {
750 return "Ed25519";
751 }
752 if ("Ed448".equals(keyAlgorithm) || "1.3.101.113".equals(keyAlgorithm)) {
753 return "Ed448";
754 }
755 if ("EdDSA".equals(keyAlgorithm)) {
756 byte[] encoded = key.getEncoded();
757 if (encoded.length <= 44) {
758 return "Ed25519";
759 }
760 if (encoded.length <= 69) {
761 return "Ed448";
762 }
763 }
764 if ("ML-DSA".equals(keyAlgorithm)) {
765 try {
766 Method getParams = key.getClass().getMethod("getParams");
767 Object params = getParams.invoke(key);
768 Method getName = params.getClass().getMethod("getName");
769 return (String) getName.invoke(params);
770 } catch (Exception e) {
771 throw new IllegalArgumentException("Cannot get algorithm name for ML-DSA key", e);
772 }
773 }
774 if ("ML-KEM".equals(keyAlgorithm)) {
775 throw new IllegalArgumentException("ML-KEM keys cannot be used for signing");
776 }
777 throw new IllegalArgumentException("Don't know what signature algorithm is best for " + key);
778 }
779
780 private KeyPair generateKeyPair() throws GeneralSecurityException {
781 return algorithm.generateKeyPair(getSecureRandom());
782 }
783
784 private V3TBSCertificateGenerator createCertBuilder(
785 X500Principal issuer, X500Principal subject, KeyPair keyPair, String signAlg) {
786 BigInteger serial = this.serial != null ? this.serial : new BigInteger(159, getSecureRandom());
787 PublicKey pubKey = keyPair.getPublic();
788
789 V3TBSCertificateGenerator generator = new V3TBSCertificateGenerator();
790 generator.setIssuer(X500Name.getInstance(issuer.getEncoded()));
791 if (subject != null) {
792 generator.setSubject(X500Name.getInstance(subject.getEncoded()));
793 }
794 generator.setSerialNumber(new ASN1Integer(serial));
795 generator.setSignature(new AlgorithmIdentifier(new ASN1ObjectIdentifier(
796 Algorithms.oidForAlgorithmName(signAlg))));
797 generator.setStartDate(new Time(Date.from(notBefore)));
798 generator.setEndDate(new Time(Date.from(notAfter)));
799 generator.setSubjectPublicKeyInfo(SubjectPublicKeyInfo.getInstance(pubKey.getEncoded()));
800 return generator;
801 }
802
803 private static byte[] tbsCertToBytes(V3TBSCertificateGenerator generator) {
804 try {
805 return generator.generateTBSCertificate().getEncoded("DER");
806 } catch (IOException e) {
807 throw new UncheckedIOException(e);
808 }
809 }
810
811 private SecureRandom getSecureRandom() {
812 SecureRandom rng = random;
813 if (rng == null) {
814 rng = SecureRandomHolder.RANDOM;
815 }
816 return rng;
817 }
818
819 private void addExtensions(V3TBSCertificateGenerator tbsCert) throws Exception {
820 ExtensionsGenerator generator = new ExtensionsGenerator();
821 if (isCertificateAuthority) {
822 final BasicConstraints basicConstraints;
823 if (pathLengthConstraint.isPresent()) {
824 basicConstraints = new BasicConstraints(pathLengthConstraint.getAsInt());
825 } else {
826 basicConstraints = new BasicConstraints(true);
827 }
828 final byte[] basicConstraintsBytes = basicConstraints.getEncoded("DER");
829 generator.addExtension(new Extension(Extension.basicConstraints, true, basicConstraintsBytes));
830 }
831 if (keyUsage != null) {
832 generator.addExtension(keyUsage);
833 }
834
835 if (!extendedKeyUsage.isEmpty()) {
836 KeyPurposeId[] usages = new KeyPurposeId[extendedKeyUsage.size()];
837 String[] usagesStrings = extendedKeyUsage.toArray(EmptyArrays.EMPTY_STRINGS);
838 for (int i = 0; i < usagesStrings.length; i++) {
839 usages[i] = KeyPurposeId.getInstance(new ASN1ObjectIdentifier(usagesStrings[i]));
840 }
841 byte[] der = new org.bouncycastle.asn1.x509.ExtendedKeyUsage(usages).getEncoded("DER");
842 generator.addExtension(new Extension(Extension.extendedKeyUsage, false, der));
843 }
844
845 if (!subjectAlternativeNames.isEmpty()) {
846
847 boolean critical = subject.getName().isEmpty();
848 byte[] result;
849 GeneralNames generalNames = new GeneralNames(subjectAlternativeNames.toArray(EMPTY_GENERAL_NAMES));
850 try {
851 result = generalNames.getEncoded("DER");
852 } catch (IOException e) {
853 throw new UncheckedIOException(e);
854 }
855 generator.addExtension(new Extension(Extension.subjectAlternativeName, critical, result));
856 }
857
858 if (!crlDistributionPoints.isEmpty()) {
859 generator.addExtension(Extension.create(
860 Extension.cRLDistributionPoints,
861 false,
862 new CRLDistPoint(crlDistributionPoints.toArray(EMPTY_DIST_POINTS))));
863 }
864
865 for (BuilderCallback callback : modifierCallbacks) {
866 callback.modify(generator);
867 }
868
869 if (!generator.isEmpty()) {
870 tbsCert.setExtensions(generator.generate());
871 }
872 }
873
874
875
876
877
878 public enum Algorithm {
879
880
881
882
883
884
885 ecp256("EC", new ECGenParameterSpec("secp256r1"), "SHA256withECDSA"),
886
887
888
889
890
891
892 ecp384("EC", new ECGenParameterSpec("secp384r1"), "SHA384withECDSA"),
893
894
895
896
897
898
899 rsa2048("RSA", new RSAKeyGenParameterSpec(2048, RSAKeyGenParameterSpec.F4), "SHA256withRSA"),
900
901
902
903
904
905
906 rsa3072("RSA", new RSAKeyGenParameterSpec(3072, RSAKeyGenParameterSpec.F4), "SHA256withRSA"),
907
908
909
910
911
912
913 rsa4096("RSA", new RSAKeyGenParameterSpec(4096, RSAKeyGenParameterSpec.F4), "SHA384withRSA"),
914
915
916
917
918
919
920 rsa8192("RSA", new RSAKeyGenParameterSpec(8192, RSAKeyGenParameterSpec.F4), "SHA384withRSA"),
921
922
923
924
925
926
927 ed25519("Ed25519", namedParameterSpec("Ed25519"), "Ed25519"),
928
929
930
931
932
933
934 ed448("Ed448", namedParameterSpec("Ed448"), "Ed448"),
935
936
937
938
939
940
941
942 mlDsa44("ML-DSA", namedParameterSpec("ML-DSA-44"), "ML-DSA-44"),
943
944
945
946
947
948
949
950 mlDsa65("ML-DSA", namedParameterSpec("ML-DSA-65"), "ML-DSA-65"),
951
952
953
954
955
956
957
958 mlDsa87("ML-DSA", namedParameterSpec("ML-DSA-87"), "ML-DSA-87"),
959
960
961
962
963
964
965
966 mlKem512("ML-KEM", namedParameterSpec("ML-KEM-512"), UNSUPPORTED_SIGN),
967
968
969
970
971
972
973
974 mlKem768("ML-KEM", namedParameterSpec("ML-KEM-768"), UNSUPPORTED_SIGN),
975
976
977
978
979
980
981
982 mlKem1024("ML-KEM", namedParameterSpec("ML-KEM-1024"), UNSUPPORTED_SIGN);
983
984 final String keyType;
985 final AlgorithmParameterSpec parameterSpec;
986 final String signatureType;
987
988 Algorithm(String keyType, AlgorithmParameterSpec parameterSpec, String signatureType) {
989 this.keyType = keyType;
990 this.parameterSpec = parameterSpec;
991 this.signatureType = signatureType;
992 }
993
994 private static AlgorithmParameterSpec namedParameterSpec(String name) {
995 try {
996 Class<?> cls = Class.forName("java.security.spec.NamedParameterSpec");
997 return (AlgorithmParameterSpec) cls.getConstructor(String.class).newInstance(name);
998 } catch (Exception e) {
999 if ("Ed25519".equals(name)) {
1000 return new EdDSAParameterSpec(EdDSAParameterSpec.Ed25519);
1001 }
1002 if ("Ed448".equals(name)) {
1003 return new EdDSAParameterSpec(EdDSAParameterSpec.Ed448);
1004 }
1005 return UNSUPPORTED_SPEC;
1006 }
1007 }
1008
1009
1010
1011
1012
1013
1014
1015
1016 public KeyPair generateKeyPair(SecureRandom secureRandom)
1017 throws GeneralSecurityException {
1018 requireNonNull(secureRandom, "secureRandom");
1019
1020 if (parameterSpec == UNSUPPORTED_SPEC) {
1021 throw new UnsupportedOperationException("This algorithm is not supported: " + this);
1022 }
1023
1024 KeyPairGenerator keyGen = Algorithms.keyPairGenerator(keyType, parameterSpec, secureRandom);
1025 return keyGen.generateKeyPair();
1026 }
1027
1028
1029
1030
1031
1032 public boolean isSupported() {
1033 return parameterSpec != UNSUPPORTED_SPEC;
1034 }
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046 public boolean supportSigning() {
1047 return !Objects.equals(signatureType, UNSUPPORTED_SIGN);
1048 }
1049 }
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060 public enum KeyUsage {
1061
1062
1063
1064
1065 digitalSignature(0),
1066
1067
1068
1069
1070
1071
1072 contentCommitment(1),
1073
1074
1075
1076 keyEncipherment(2),
1077
1078
1079
1080 dataEncipherment(3),
1081
1082
1083
1084 keyAgreement(4),
1085
1086
1087
1088
1089
1090
1091 keyCertSign(5),
1092
1093
1094
1095
1096
1097
1098 cRLSign(6),
1099
1100
1101
1102
1103
1104 encipherOnly(7),
1105
1106
1107
1108
1109
1110 decipherOnly(8);
1111
1112 private final int bitId;
1113
1114 KeyUsage(int bitId) {
1115 this.bitId = bitId;
1116 }
1117 }
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132 public enum ExtendedKeyUsage {
1133
1134
1135
1136 PKIX_KP_SERVER_AUTH(OID_PKIX_KP_SERVER_AUTH),
1137
1138
1139
1140 PKIX_KP_CLIENT_AUTH(OID_PKIX_KP_CLIENT_AUTH),
1141
1142
1143
1144 PKIX_KP_CODE_SIGNING(OID_PKIX_KP_CODE_SIGNING),
1145
1146
1147
1148 PKIX_KP_EMAIL_PROTECTION(OID_PKIX_KP_EMAIL_PROTECTION),
1149
1150
1151
1152 PKIX_KP_TIME_STAMPING(OID_PKIX_KP_TIME_STAMPING),
1153
1154
1155
1156 PKIX_KP_OCSP_SIGNING(OID_PKIX_KP_OCSP_SIGNING),
1157
1158
1159
1160 KERBEROS_KEY_PURPOSE_CLIENT_AUTH(OID_KERBEROS_KEY_PURPOSE_CLIENT_AUTH),
1161
1162
1163
1164 MICROSOFT_SMARTCARD_LOGIN(OID_MICROSOFT_SMARTCARD_LOGIN);
1165
1166 private final String oid;
1167
1168 ExtendedKeyUsage(String oid) {
1169 this.oid = oid;
1170 }
1171
1172 public String getOid() {
1173 return oid;
1174 }
1175 }
1176
1177 @FunctionalInterface
1178 private interface BuilderCallback {
1179 void modify(ExtensionsGenerator builder) throws Exception;
1180 }
1181
1182 private static final class SecureRandomHolder {
1183 private static final SecureRandom RANDOM = new SecureRandom();
1184 }
1185 }