3636import java .security .cert .X509Certificate ;
3737import java .security .spec .ECGenParameterSpec ;
3838import java .security .spec .RSAKeyGenParameterSpec ;
39+ import java .time .Duration ;
3940import java .util .Date ;
4041import org .bouncycastle .asn1 .DERIA5String ;
4142import org .bouncycastle .asn1 .DEROctetString ;
4243import org .bouncycastle .asn1 .pkcs .PKCSObjectIdentifiers ;
4344import org .bouncycastle .asn1 .x500 .X500Name ;
45+ import org .bouncycastle .asn1 .x500 .X500NameBuilder ;
46+ import org .bouncycastle .asn1 .x500 .style .BCStyle ;
4447import org .bouncycastle .asn1 .x509 .AlgorithmIdentifier ;
4548import org .bouncycastle .asn1 .x509 .BasicConstraints ;
4649import org .bouncycastle .asn1 .x509 .ExtendedKeyUsage ;
5356import org .bouncycastle .cert .X509CertificateHolder ;
5457import org .bouncycastle .cert .X509v3CertificateBuilder ;
5558import org .bouncycastle .cert .jcajce .JcaX509CertificateConverter ;
59+ import org .bouncycastle .cert .jcajce .JcaX509v3CertificateBuilder ;
5660import org .bouncycastle .crypto .params .AsymmetricKeyParameter ;
5761import org .bouncycastle .crypto .util .PrivateKeyFactory ;
5862import org .bouncycastle .jce .provider .BouncyCastleProvider ;
@@ -85,6 +89,27 @@ public class X509TestHelpers {
8589 // Per RFC 5280 section 4.1.2.2, X509 certificates can use up to 20 bytes == 160 bits for serial numbers.
8690 private static final int SERIAL_NUMBER_MAX_BITS = 20 * Byte .SIZE ;
8791
92+ public static X509Certificate newSelfSignedCert (String name , KeyPair keyPair ) throws IOException , OperatorCreationException , GeneralSecurityException {
93+ X500NameBuilder caNameBuilder = new X500NameBuilder (BCStyle .INSTANCE );
94+ caNameBuilder .addRDN (BCStyle .CN , name );
95+ return newSelfSignedCACert (caNameBuilder .build (), keyPair , Duration .ofDays (1 ).toMillis ());
96+ }
97+
98+ @ FunctionalInterface
99+ public interface CertificateCustomization {
100+ void customize (X509v3CertificateBuilder builder ) throws Exception ;
101+ }
102+
103+ public static X509Certificate newCert (X509Certificate caCert , KeyPair caKeyPair , String name , PublicKey certPublicKey , CertificateCustomization customization ) throws Exception {
104+ X500NameBuilder nameBuilder = new X500NameBuilder (BCStyle .INSTANCE );
105+ nameBuilder .addRDN (BCStyle .CN , name );
106+ return newCert (caCert , caKeyPair , nameBuilder .build (), certPublicKey , Duration .ofDays (1 ).toMillis (), customization );
107+ }
108+
109+ public static X509Certificate newCert (X509Certificate caCert , KeyPair caKeyPair , String name , PublicKey certPublicKey ) throws Exception {
110+ return newCert (caCert , caKeyPair , name , certPublicKey , null );
111+ }
112+
88113 /**
89114 * Uses the private key of the given key pair to create a self-signed CA certificate with the public half of the
90115 * key pair and the given subject and expiration. The issuer of the new cert will be equal to the subject.
@@ -102,6 +127,11 @@ public class X509TestHelpers {
102127 */
103128 public static X509Certificate newSelfSignedCACert (
104129 X500Name subject , KeyPair keyPair , long expirationMillis ) throws IOException , OperatorCreationException , GeneralSecurityException {
130+ return newSelfSignedCACert (subject , keyPair , expirationMillis , null );
131+ }
132+
133+ public static X509Certificate newSelfSignedCACert (
134+ X500Name subject , KeyPair keyPair , long expirationMillis , CertificateCustomization customization ) throws IOException , OperatorCreationException , GeneralSecurityException {
105135 Date now = new Date ();
106136 X509v3CertificateBuilder builder = initCertBuilder (subject , // for self-signed certs, issuer == subject
107137 now , new Date (now .getTime ()
@@ -129,7 +159,12 @@ now, new Date(now.getTime()
129159 * @throws GeneralSecurityException
130160 */
131161 public static X509Certificate newCert (
132- X509Certificate caCert , KeyPair caKeyPair , X500Name certSubject , PublicKey certPublicKey , long expirationMillis ) throws IOException , OperatorCreationException , GeneralSecurityException {
162+ X509Certificate caCert , KeyPair caKeyPair , X500Name certSubject , PublicKey certPublicKey , long expirationMillis ) throws Exception {
163+ return newCert (caCert , caKeyPair , certSubject , certPublicKey , expirationMillis , null );
164+ }
165+
166+ public static X509Certificate newCert (
167+ X509Certificate caCert , KeyPair caKeyPair , X500Name certSubject , PublicKey certPublicKey , long expirationMillis , CertificateCustomization customization ) throws Exception {
133168 if (!caKeyPair .getPublic ().equals (caCert .getPublicKey ())) {
134169 throw new IllegalArgumentException ("CA private key does not match the public key in the CA cert" );
135170 }
@@ -143,6 +178,9 @@ public static X509Certificate newCert(
143178 builder .addExtension (Extension .extendedKeyUsage , true , new ExtendedKeyUsage (new KeyPurposeId []{KeyPurposeId .id_kp_serverAuth , KeyPurposeId .id_kp_clientAuth }));
144179
145180 builder .addExtension (Extension .subjectAlternativeName , false , getLocalhostSubjectAltNames ());
181+ if (customization != null ) {
182+ customization .customize (builder );
183+ }
146184 return buildAndSignCertificate (caKeyPair .getPrivate (), builder );
147185 }
148186
@@ -172,7 +210,7 @@ private static GeneralNames getLocalhostSubjectAltNames() throws UnknownHostExce
172210 */
173211 private static X509v3CertificateBuilder initCertBuilder (
174212 X500Name issuer , Date notBefore , Date notAfter , X500Name subject , PublicKey subjectPublicKey ) {
175- return new X509v3CertificateBuilder (issuer , new BigInteger (SERIAL_NUMBER_MAX_BITS , PRNG ), notBefore , notAfter , subject , SubjectPublicKeyInfo .getInstance (subjectPublicKey .getEncoded ()));
213+ return new JcaX509v3CertificateBuilder (issuer , new BigInteger (SERIAL_NUMBER_MAX_BITS , PRNG ), notBefore , notAfter , subject , SubjectPublicKeyInfo .getInstance (subjectPublicKey .getEncoded ()));
176214 }
177215
178216 /**
0 commit comments