20
20
import org .bouncycastle .asn1 .x509 .ExtensionsGenerator ;
21
21
import org .bouncycastle .asn1 .x509 .GeneralName ;
22
22
import org .bouncycastle .asn1 .x509 .GeneralNames ;
23
+ import org .bouncycastle .asn1 .x509 .KeyUsage ;
23
24
import org .bouncycastle .asn1 .x509 .Time ;
24
25
import org .bouncycastle .cert .CertIOException ;
25
26
import org .bouncycastle .cert .X509CertificateHolder ;
53
54
import java .sql .Date ;
54
55
import java .time .ZoneOffset ;
55
56
import java .time .ZonedDateTime ;
57
+ import java .util .Collection ;
58
+ import java .util .Collections ;
56
59
import java .util .HashSet ;
57
60
import java .util .Locale ;
61
+ import java .util .Map ;
58
62
import java .util .Objects ;
59
63
import java .util .Set ;
64
+ import java .util .TreeMap ;
60
65
61
66
import javax .net .ssl .X509ExtendedKeyManager ;
62
67
import javax .net .ssl .X509ExtendedTrustManager ;
@@ -73,14 +78,33 @@ public class CertGenUtils {
73
78
private static final int SERIAL_BIT_LENGTH = 20 * 8 ;
74
79
private static final BouncyCastleProvider BC_PROV = new BouncyCastleProvider ();
75
80
81
+ /**
82
+ * The mapping of key usage names to their corresponding integer values as defined in {@code KeyUsage} class.
83
+ */
84
+ public static final Map <String , Integer > KEY_USAGE_MAPPINGS = Collections .unmodifiableMap (
85
+ new TreeMap <>(
86
+ Map .ofEntries (
87
+ Map .entry ("digitalSignature" , KeyUsage .digitalSignature ),
88
+ Map .entry ("nonRepudiation" , KeyUsage .nonRepudiation ),
89
+ Map .entry ("keyEncipherment" , KeyUsage .keyEncipherment ),
90
+ Map .entry ("dataEncipherment" , KeyUsage .dataEncipherment ),
91
+ Map .entry ("keyAgreement" , KeyUsage .keyAgreement ),
92
+ Map .entry ("keyCertSign" , KeyUsage .keyCertSign ),
93
+ Map .entry ("cRLSign" , KeyUsage .cRLSign ),
94
+ Map .entry ("encipherOnly" , KeyUsage .encipherOnly ),
95
+ Map .entry ("decipherOnly" , KeyUsage .decipherOnly )
96
+ )
97
+ )
98
+ );
99
+
76
100
private CertGenUtils () {}
77
101
78
102
/**
79
103
* Generates a CA certificate
80
104
*/
81
- public static X509Certificate generateCACertificate (X500Principal x500Principal , KeyPair keyPair , int days )
105
+ public static X509Certificate generateCACertificate (X500Principal x500Principal , KeyPair keyPair , int days , KeyUsage keyUsage )
82
106
throws OperatorCreationException , CertificateException , CertIOException , NoSuchAlgorithmException {
83
- return generateSignedCertificate (x500Principal , null , keyPair , null , null , true , days , null );
107
+ return generateSignedCertificate (x500Principal , null , keyPair , null , null , true , days , null , keyUsage , Set . of () );
84
108
}
85
109
86
110
/**
@@ -107,7 +131,7 @@ public static X509Certificate generateSignedCertificate(
107
131
PrivateKey caPrivKey ,
108
132
int days
109
133
) throws OperatorCreationException , CertificateException , CertIOException , NoSuchAlgorithmException {
110
- return generateSignedCertificate (principal , subjectAltNames , keyPair , caCert , caPrivKey , false , days , null );
134
+ return generateSignedCertificate (principal , subjectAltNames , keyPair , caCert , caPrivKey , false , days , null , null , Set . of () );
111
135
}
112
136
113
137
/**
@@ -123,54 +147,14 @@ public static X509Certificate generateSignedCertificate(
123
147
* certificate
124
148
* @param caPrivKey the CA private key. If {@code null}, this results in a self signed
125
149
* certificate
126
- * @param days no of days certificate will be valid from now
127
- * @param signatureAlgorithm algorithm used for signing certificate. If {@code null} or
128
- * empty, then use default algorithm {@link CertGenUtils#getDefaultSignatureAlgorithm(PrivateKey)}
129
- * @return a signed {@link X509Certificate}
130
- */
131
- public static X509Certificate generateSignedCertificate (
132
- X500Principal principal ,
133
- GeneralNames subjectAltNames ,
134
- KeyPair keyPair ,
135
- X509Certificate caCert ,
136
- PrivateKey caPrivKey ,
137
- int days ,
138
- String signatureAlgorithm
139
- ) throws OperatorCreationException , CertificateException , CertIOException , NoSuchAlgorithmException {
140
- return generateSignedCertificate (principal , subjectAltNames , keyPair , caCert , caPrivKey , false , days , signatureAlgorithm );
141
- }
142
-
143
- /**
144
- * Generates a signed certificate
145
- *
146
- * @param principal the principal of the certificate; commonly referred to as the
147
- * distinguished name (DN)
148
- * @param subjectAltNames the subject alternative names that should be added to the
149
- * certificate as an X509v3 extension. May be {@code null}
150
- * @param keyPair the key pair that will be associated with the certificate
151
- * @param caCert the CA certificate. If {@code null}, this results in a self signed
152
- * certificate
153
- * @param caPrivKey the CA private key. If {@code null}, this results in a self signed
154
- * certificate
155
150
* @param isCa whether or not the generated certificate is a CA
156
151
* @param days no of days certificate will be valid from now
157
152
* @param signatureAlgorithm algorithm used for signing certificate. If {@code null} or
158
153
* empty, then use default algorithm {@link CertGenUtils#getDefaultSignatureAlgorithm(PrivateKey)}
154
+ * @param keyUsage the key usage that should be added to the certificate as a X509v3 extension (can be {@code null})
155
+ * @param extendedKeyUsages the extended key usages that should be added to the certificate as a X509v3 extension (can be empty)
159
156
* @return a signed {@link X509Certificate}
160
157
*/
161
- public static X509Certificate generateSignedCertificate (
162
- X500Principal principal ,
163
- GeneralNames subjectAltNames ,
164
- KeyPair keyPair ,
165
- X509Certificate caCert ,
166
- PrivateKey caPrivKey ,
167
- boolean isCa ,
168
- int days ,
169
- String signatureAlgorithm
170
- ) throws NoSuchAlgorithmException , CertificateException , CertIOException , OperatorCreationException {
171
- return generateSignedCertificate (principal , subjectAltNames , keyPair , caCert , caPrivKey , isCa , days , signatureAlgorithm , Set .of ());
172
- }
173
-
174
158
public static X509Certificate generateSignedCertificate (
175
159
X500Principal principal ,
176
160
GeneralNames subjectAltNames ,
@@ -180,6 +164,7 @@ public static X509Certificate generateSignedCertificate(
180
164
boolean isCa ,
181
165
int days ,
182
166
String signatureAlgorithm ,
167
+ KeyUsage keyUsage ,
183
168
Set <ExtendedKeyUsage > extendedKeyUsages
184
169
) throws NoSuchAlgorithmException , CertificateException , CertIOException , OperatorCreationException {
185
170
Objects .requireNonNull (keyPair , "Key-Pair must not be null" );
@@ -198,6 +183,7 @@ public static X509Certificate generateSignedCertificate(
198
183
notBefore ,
199
184
notAfter ,
200
185
signatureAlgorithm ,
186
+ keyUsage ,
201
187
extendedKeyUsages
202
188
);
203
189
}
@@ -223,6 +209,7 @@ public static X509Certificate generateSignedCertificate(
223
209
notBefore ,
224
210
notAfter ,
225
211
signatureAlgorithm ,
212
+ null ,
226
213
Set .of ()
227
214
);
228
215
}
@@ -237,6 +224,7 @@ public static X509Certificate generateSignedCertificate(
237
224
ZonedDateTime notBefore ,
238
225
ZonedDateTime notAfter ,
239
226
String signatureAlgorithm ,
227
+ KeyUsage keyUsage ,
240
228
Set <ExtendedKeyUsage > extendedKeyUsages
241
229
) throws NoSuchAlgorithmException , CertIOException , OperatorCreationException , CertificateException {
242
230
final BigInteger serial = CertGenUtils .getSerial ();
@@ -272,6 +260,11 @@ public static X509Certificate generateSignedCertificate(
272
260
}
273
261
builder .addExtension (Extension .basicConstraints , isCa , new BasicConstraints (isCa ));
274
262
263
+ if (keyUsage != null ) {
264
+ // as per RFC 5280 (section 4.2.1.3), if the key usage is present, then it SHOULD be marked as critical.
265
+ final boolean isCritical = true ;
266
+ builder .addExtension (Extension .keyUsage , isCritical , keyUsage );
267
+ }
275
268
if (extendedKeyUsages != null ) {
276
269
for (ExtendedKeyUsage extendedKeyUsage : extendedKeyUsages ) {
277
270
builder .addExtension (Extension .extendedKeyUsage , false , extendedKeyUsage );
@@ -318,7 +311,7 @@ private static String getDefaultSignatureAlgorithm(PrivateKey key) {
318
311
*/
319
312
static PKCS10CertificationRequest generateCSR (KeyPair keyPair , X500Principal principal , GeneralNames sanList ) throws IOException ,
320
313
OperatorCreationException {
321
- return generateCSR (keyPair , principal , sanList , Set .of ());
314
+ return generateCSR (keyPair , principal , sanList , null , Set .of ());
322
315
}
323
316
324
317
/**
@@ -335,6 +328,7 @@ static PKCS10CertificationRequest generateCSR(
335
328
KeyPair keyPair ,
336
329
X500Principal principal ,
337
330
GeneralNames sanList ,
331
+ KeyUsage keyUsage ,
338
332
Set <ExtendedKeyUsage > extendedKeyUsages
339
333
) throws IOException , OperatorCreationException {
340
334
Objects .requireNonNull (keyPair , "Key-Pair must not be null" );
@@ -347,7 +341,9 @@ static PKCS10CertificationRequest generateCSR(
347
341
if (sanList != null ) {
348
342
extGen .addExtension (Extension .subjectAlternativeName , false , sanList );
349
343
}
350
-
344
+ if (keyUsage != null ) {
345
+ extGen .addExtension (Extension .keyUsage , true , keyUsage );
346
+ }
351
347
for (ExtendedKeyUsage extendedKeyUsage : extendedKeyUsages ) {
352
348
extGen .addExtension (Extension .extendedKeyUsage , false , extendedKeyUsage );
353
349
}
@@ -430,4 +426,31 @@ public static GeneralName createCommonName(String cn) {
430
426
public static String buildDnFromDomain (String domain ) {
431
427
return "DC=" + domain .replace ("." , ",DC=" );
432
428
}
429
+
430
+ public static KeyUsage buildKeyUsage (Collection <String > keyUsages ) {
431
+ if (keyUsages == null || keyUsages .isEmpty ()) {
432
+ return null ;
433
+ }
434
+
435
+ int usageBits = 0 ;
436
+ for (String keyUsageName : keyUsages ) {
437
+ Integer keyUsageValue = findKeyUsageByName (keyUsageName );
438
+ if (keyUsageValue == null ) {
439
+ throw new IllegalArgumentException ("Unknown keyUsage: " + keyUsageName );
440
+ }
441
+ usageBits |= keyUsageValue ;
442
+ }
443
+ return new KeyUsage (usageBits );
444
+ }
445
+
446
+ public static boolean isValidKeyUsage (String keyUsage ) {
447
+ return findKeyUsageByName (keyUsage ) != null ;
448
+ }
449
+
450
+ private static Integer findKeyUsageByName (String keyUsageName ) {
451
+ if (keyUsageName == null ) {
452
+ return null ;
453
+ }
454
+ return KEY_USAGE_MAPPINGS .get (keyUsageName .trim ());
455
+ }
433
456
}
0 commit comments