2020import  org .bouncycastle .asn1 .x509 .ExtensionsGenerator ;
2121import  org .bouncycastle .asn1 .x509 .GeneralName ;
2222import  org .bouncycastle .asn1 .x509 .GeneralNames ;
23+ import  org .bouncycastle .asn1 .x509 .KeyUsage ;
2324import  org .bouncycastle .asn1 .x509 .Time ;
2425import  org .bouncycastle .cert .CertIOException ;
2526import  org .bouncycastle .cert .X509CertificateHolder ;
5354import  java .sql .Date ;
5455import  java .time .ZoneOffset ;
5556import  java .time .ZonedDateTime ;
57+ import  java .util .Collection ;
58+ import  java .util .Collections ;
5659import  java .util .HashSet ;
5760import  java .util .Locale ;
61+ import  java .util .Map ;
5862import  java .util .Objects ;
5963import  java .util .Set ;
64+ import  java .util .TreeMap ;
6065
6166import  javax .net .ssl .X509ExtendedKeyManager ;
6267import  javax .net .ssl .X509ExtendedTrustManager ;
@@ -73,14 +78,33 @@ public class CertGenUtils {
7378    private  static  final  int  SERIAL_BIT_LENGTH  = 20  * 8 ;
7479    private  static  final  BouncyCastleProvider  BC_PROV  = new  BouncyCastleProvider ();
7580
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+ 
76100    private  CertGenUtils () {}
77101
78102    /** 
79103     * Generates a CA certificate 
80104     */ 
81-     public  static  X509Certificate  generateCACertificate (X500Principal  x500Principal , KeyPair  keyPair , int  days )
105+     public  static  X509Certificate  generateCACertificate (X500Principal  x500Principal , KeyPair  keyPair , int  days ,  KeyUsage   keyUsage )
82106        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 () );
84108    }
85109
86110    /** 
@@ -107,7 +131,7 @@ public static X509Certificate generateSignedCertificate(
107131        PrivateKey  caPrivKey ,
108132        int  days 
109133    ) 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 () );
111135    }
112136
113137    /** 
@@ -123,54 +147,14 @@ public static X509Certificate generateSignedCertificate(
123147     *                           certificate 
124148     * @param caPrivKey          the CA private key. If {@code null}, this results in a self signed 
125149     *                           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 
155150     * @param isCa               whether or not the generated certificate is a CA 
156151     * @param days               no of days certificate will be valid from now 
157152     * @param signatureAlgorithm algorithm used for signing certificate. If {@code null} or 
158153     *                           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) 
159156     * @return a signed {@link X509Certificate} 
160157     */ 
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- 
174158    public  static  X509Certificate  generateSignedCertificate (
175159        X500Principal  principal ,
176160        GeneralNames  subjectAltNames ,
@@ -180,6 +164,7 @@ public static X509Certificate generateSignedCertificate(
180164        boolean  isCa ,
181165        int  days ,
182166        String  signatureAlgorithm ,
167+         KeyUsage  keyUsage ,
183168        Set <ExtendedKeyUsage > extendedKeyUsages 
184169    ) throws  NoSuchAlgorithmException , CertificateException , CertIOException , OperatorCreationException  {
185170        Objects .requireNonNull (keyPair , "Key-Pair must not be null" );
@@ -198,6 +183,7 @@ public static X509Certificate generateSignedCertificate(
198183            notBefore ,
199184            notAfter ,
200185            signatureAlgorithm ,
186+             keyUsage ,
201187            extendedKeyUsages 
202188        );
203189    }
@@ -223,6 +209,7 @@ public static X509Certificate generateSignedCertificate(
223209            notBefore ,
224210            notAfter ,
225211            signatureAlgorithm ,
212+             null ,
226213            Set .of ()
227214        );
228215    }
@@ -237,6 +224,7 @@ public static X509Certificate generateSignedCertificate(
237224        ZonedDateTime  notBefore ,
238225        ZonedDateTime  notAfter ,
239226        String  signatureAlgorithm ,
227+         KeyUsage  keyUsage ,
240228        Set <ExtendedKeyUsage > extendedKeyUsages 
241229    ) throws  NoSuchAlgorithmException , CertIOException , OperatorCreationException , CertificateException  {
242230        final  BigInteger  serial  = CertGenUtils .getSerial ();
@@ -272,6 +260,11 @@ public static X509Certificate generateSignedCertificate(
272260        }
273261        builder .addExtension (Extension .basicConstraints , isCa , new  BasicConstraints (isCa ));
274262
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+         }
275268        if  (extendedKeyUsages  != null ) {
276269            for  (ExtendedKeyUsage  extendedKeyUsage  : extendedKeyUsages ) {
277270                builder .addExtension (Extension .extendedKeyUsage , false , extendedKeyUsage );
@@ -318,7 +311,7 @@ private static String getDefaultSignatureAlgorithm(PrivateKey key) {
318311     */ 
319312    static  PKCS10CertificationRequest  generateCSR (KeyPair  keyPair , X500Principal  principal , GeneralNames  sanList ) throws  IOException ,
320313        OperatorCreationException  {
321-         return  generateCSR (keyPair , principal , sanList , Set .of ());
314+         return  generateCSR (keyPair , principal , sanList , null ,  Set .of ());
322315    }
323316
324317    /** 
@@ -335,6 +328,7 @@ static PKCS10CertificationRequest generateCSR(
335328        KeyPair  keyPair ,
336329        X500Principal  principal ,
337330        GeneralNames  sanList ,
331+         KeyUsage  keyUsage ,
338332        Set <ExtendedKeyUsage > extendedKeyUsages 
339333    ) throws  IOException , OperatorCreationException  {
340334        Objects .requireNonNull (keyPair , "Key-Pair must not be null" );
@@ -347,7 +341,9 @@ static PKCS10CertificationRequest generateCSR(
347341        if  (sanList  != null ) {
348342            extGen .addExtension (Extension .subjectAlternativeName , false , sanList );
349343        }
350- 
344+         if  (keyUsage  != null ) {
345+             extGen .addExtension (Extension .keyUsage , true , keyUsage );
346+         }
351347        for  (ExtendedKeyUsage  extendedKeyUsage  : extendedKeyUsages ) {
352348            extGen .addExtension (Extension .extendedKeyUsage , false , extendedKeyUsage );
353349        }
@@ -430,4 +426,31 @@ public static GeneralName createCommonName(String cn) {
430426    public  static  String  buildDnFromDomain (String  domain ) {
431427        return  "DC="  + domain .replace ("." , ",DC=" );
432428    }
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+     }
433456}
0 commit comments