DIRSERVER-2326 Use Bouncy Castle to generate certificates (#40)

* Use Bouncy Castle to generate certificates

* Make OSGI happy with the BouncyCastle certificate generator

* Add SAN extension
diff --git a/all/pom.xml b/all/pom.xml
index 96cd601..533c931 100644
--- a/all/pom.xml
+++ b/all/pom.xml
@@ -163,11 +163,11 @@
               <promoteTransitiveDependencies>true</promoteTransitiveDependencies>
               <filters>
                 <filter>
-                  <artifact>org.bouncycastle:bcprov-jdk15on</artifact>
+                  <artifact>*:*</artifact>
                   <excludes>
-                    <exclude>META-INF/BCK*.SF</exclude>
-                    <exclude>META-INF/BCK*.DSA</exclude>
-                    <exclude>META-INF/BCK*.RSA</exclude>
+                    <exclude>META-INF/*.SF</exclude>
+                    <exclude>META-INF/*.DSA</exclude>
+                    <exclude>META-INF/*.RSA</exclude>
                   </excludes>
                 </filter>
               </filters>
diff --git a/core/pom.xml b/core/pom.xml
index 1821f93..393f52e 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -158,6 +158,11 @@
       <groupId>org.bouncycastle</groupId>
       <artifactId>bcprov-jdk15on</artifactId>
     </dependency>
+
+    <dependency>
+      <groupId>org.bouncycastle</groupId>
+      <artifactId>bcpkix-jdk15on</artifactId>
+    </dependency>
   </dependencies>
 
   <build>
@@ -261,6 +266,10 @@
                 org.bouncycastle.x509;version=${bcprov.version},
                 org.bouncycastle.asn1;version=${bcprov.version},
                 org.bouncycastle.asn1.x509;version=${bcprov.version},
+                org.bouncycastle.operator;version=${bcprov.version},
+                org.bouncycastle.operator.jcajce;version=${bcprov.version},
+                org.bouncycastle.cert;version=${bcprov.version},
+                org.bouncycastle.cert.jcajce;version=${bcprov.version},
                 org.slf4j;version=${slf4j.api.bundleversion}
             </Import-Package>
           </instructions>
diff --git a/core/src/main/java/org/apache/directory/server/core/security/CertificateUtil.java b/core/src/main/java/org/apache/directory/server/core/security/CertificateUtil.java
index be1e40f..11ada86 100644
--- a/core/src/main/java/org/apache/directory/server/core/security/CertificateUtil.java
+++ b/core/src/main/java/org/apache/directory/server/core/security/CertificateUtil.java
@@ -26,6 +26,7 @@
 import java.io.InputStream;
 import java.math.BigInteger;
 import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.security.InvalidKeyException;
@@ -40,36 +41,33 @@
 import java.security.SignatureException;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
+import java.time.Duration;
+import java.time.Instant;
 import java.util.Date;
 import java.util.Enumeration;
 
 import javax.net.ssl.KeyManagerFactory;
+import javax.security.auth.x500.X500Principal;
 
 import org.apache.directory.api.util.Strings;
-
-import sun.security.x509.AlgorithmId;
-import sun.security.x509.BasicConstraintsExtension;
-import sun.security.x509.CertificateAlgorithmId;
-import sun.security.x509.CertificateExtensions;
-import sun.security.x509.CertificateSerialNumber;
-import sun.security.x509.CertificateValidity;
-import sun.security.x509.CertificateVersion;
-import sun.security.x509.CertificateX509Key;
-import sun.security.x509.DNSName;
-import sun.security.x509.GeneralName;
-import sun.security.x509.GeneralNames;
-import sun.security.x509.IPAddressName;
-import sun.security.x509.SubjectAlternativeNameExtension;
-import sun.security.x509.X500Name;
-import sun.security.x509.X509CertImpl;
-import sun.security.x509.X509CertInfo;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.cert.CertIOException;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
 
 /**
  * Helper class used to generate self-signed certificates, and load a KeyStore
  * 
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
-@SuppressWarnings("restriction")
 public final class CertificateUtil
 {
     private static final boolean SELF_SIGNED = true;
@@ -81,94 +79,40 @@
         // Nothing to do
     }
     
-    
-    private static void setInfo( X509CertInfo info, X500Name subject, X500Name issuer, KeyPair keyPair, int days, 
-        String algoStr, boolean isCA ) 
-        throws CertificateException, IOException, NoSuchAlgorithmException
+    public static X509Certificate generateX509Certificate( X500Principal subjectDn, X500Principal issuerDn, KeyPair keyPair,
+            long daysValidity, String sigAlgorithm, boolean isCa )
+                    throws CertificateException
     {
-        Date from = new Date();
-        Date to = new Date( from.getTime() + days * 86_400_000L );
-        CertificateValidity interval = new CertificateValidity( from, to );
-
-        // Feed the certificate info structure
-        // version         [0]  EXPLICIT Version DEFAULT v1
-        // Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
-        info.set( X509CertInfo.VERSION, new CertificateVersion( CertificateVersion.V3 ) );
-        
-        // serialNumber         CertificateSerialNumber
-        // CertificateSerialNumber  ::=  INTEGER
+        Instant from = Instant.now();
+        Instant to = from.plus( Duration.ofDays( daysValidity ) );
         BigInteger serialNumber = new BigInteger( 64, new SecureRandom() );
-        info.set( X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber( serialNumber ) );
+        try
+        {
+            ContentSigner signer = new JcaContentSignerBuilder( sigAlgorithm ).build( keyPair.getPrivate() );
+            InetAddress localHost = InetAddress.getLocalHost();
+            GeneralName[] sanLocalHost = new GeneralName[] {
+                    new GeneralName( GeneralName.dNSName,
+                            localHost.getHostName() ),
+                    new GeneralName( GeneralName.iPAddress, localHost.getHostAddress() )
+            };
+            X509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder( issuerDn,
+                    serialNumber,
+                    Date.from( from ),
+                    Date.from( to ),
+                    subjectDn,
+                    keyPair.getPublic() )
+                .addExtension( Extension.basicConstraints, CRITICAL, new BasicConstraints( isCa ) )
+                .addExtension( Extension.subjectAlternativeName, false, new GeneralNames( sanLocalHost ) );
 
-        // signature            AlgorithmIdentifier
-        AlgorithmId algo = AlgorithmId.get( algoStr );
-        info.set( X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId( algo ) );
-
-        // issuer               Name
-        // Name ::= CHOICE {
-        //          RDNSequence }
-        // RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
-        // RelativeDistinguishedName ::=
-        //          SET OF AttributeTypeAndValue
-        // AttributeTypeAndValue ::= SEQUENCE {
-        //          type     AttributeType,
-        //          value    AttributeValue }
-        // AttributeType ::= OBJECT IDENTIFIER
-        // AttributeValue ::= ANY DEFINED BY AttributeType
-        info.set( X509CertInfo.ISSUER, issuer );
-        
-        // validity             Validity,
-        // Validity ::= SEQUENCE {
-        //          notBefore      Time,
-        //          notAfter       Time }
-        info.set( X509CertInfo.VALIDITY, interval );
-        
-        // subject              Name
-        // Name ::= CHOICE {
-        //          RDNSequence }
-        // RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
-        // RelativeDistinguishedName ::=
-        //          SET OF AttributeTypeAndValue
-        // AttributeTypeAndValue ::= SEQUENCE {
-        //          type     AttributeType,
-        //          value    AttributeValue }
-        // AttributeType ::= OBJECT IDENTIFIER
-        // AttributeValue ::= ANY DEFINED BY AttributeType
-        info.set( X509CertInfo.SUBJECT, subject );
-        
-        // subjectPublicKeyInfo SubjectPublicKeyInfo,
-        // SubjectPublicKeyInfo  ::=  SEQUENCE  {
-        //          algorithm            AlgorithmIdentifier,
-        //          subjectPublicKey     BIT STRING  }
-        info.set( X509CertInfo.KEY, new CertificateX509Key( keyPair.getPublic() ) );
-
-        // Extensions. Basically, a subjectAltName and a Basic-Constraint 
-        CertificateExtensions extensions = new CertificateExtensions();
-
-        // SubjectAltName
-        GeneralNames names = new GeneralNames();
-        names.add( new GeneralName( new DNSName( InetAddress.getLocalHost().getHostName() ) ) );
-        String ipAddress = InetAddress.getLocalHost().getHostAddress();
-        names.add( new GeneralName( new IPAddressName( ipAddress ) ) );
-        
-        // A wildcard
-        //names.add( new GeneralName( 
-        //    new DNSName( 
-        //        new DerValue( 
-        //            DerValue.tag_IA5String, "*.apache.org" ) ) ) );
-        SubjectAlternativeNameExtension subjectAltName = new SubjectAlternativeNameExtension( names );
-        
-        extensions.set( subjectAltName.getExtensionId().toString(), subjectAltName );
-
-        // The Basic-Constraint,
-        BasicConstraintsExtension basicConstraint = new BasicConstraintsExtension( CRITICAL, isCA, -1 );
-        extensions.set( basicConstraint.getExtensionId().toString(), basicConstraint );
-
-        // Inject the extensions into the cert
-        info.set( X509CertInfo.EXTENSIONS, extensions );
+            return new JcaX509CertificateConverter().setProvider( new BouncyCastleProvider() )
+                    .getCertificate( certificateBuilder.build( signer ) );
+        }
+        catch ( OperatorCreationException | CertIOException | UnknownHostException e )
+        {
+            throw new CertificateException( "BouncyCastle failed to generate the X509 certificate.", e );
+        }
     }
     
-    
     /**
      * Create a self signed certificate
      * 
@@ -184,23 +128,12 @@
      * @throws NoSuchProviderException  If we don't have a security provider
      * @throws InvalidKeyException  If the KeyPair is invalid
      */
-    public static X509Certificate generateSelfSignedCertificate( X500Name issuer, KeyPair keyPair,  int days, String algoStr ) 
+    public static X509Certificate generateSelfSignedCertificate( X500Principal issuer, KeyPair keyPair,  int days, String algoStr ) 
         throws CertificateException, IOException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException
     {
-        // Create the certificate info
-        X509CertInfo info = new X509CertInfo();
-        
-        // Set the common certificate info
-        setInfo( info, issuer, issuer, keyPair, days, algoStr, SELF_SIGNED );
-        
-        // Sign the cert to identify the algorithm that's used.
-        X509CertImpl certificate = new X509CertImpl( info );
-        certificate.sign( keyPair.getPrivate(), algoStr );
-
-        return certificate;
+        return generateX509Certificate( issuer, issuer, keyPair, days, algoStr, SELF_SIGNED );
     }
     
-    
     /**
      * Generate a Certificate signed by a CA certificate
      * 
@@ -216,20 +149,10 @@
      * @throws NoSuchProviderException  If we don't have a security provider
      * @throws InvalidKeyException  If the KeyPair is invalid
      */
-    public static X509Certificate generateCertificate( X500Name subject, X500Name issuer, KeyPair keyPair,  int days, String algoStr ) 
+    public static X509Certificate generateCertificate( X500Principal subject, X500Principal issuer, KeyPair keyPair,  int days, String algoStr ) 
         throws CertificateException, IOException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException
     {
-        // Create the certificate info
-        X509CertInfo info = new X509CertInfo();
-        
-        // Set the common certificate info
-        setInfo( info, subject, issuer, keyPair, days, algoStr, CA_SIGNED );
-         
-        // Sign the cert to identify the algorithm that's used.
-        X509CertImpl certificate = new X509CertImpl( info );
-        certificate.sign( keyPair.getPrivate(), algoStr );
-
-        return certificate;
+        return generateX509Certificate( subject, issuer, keyPair, days, algoStr, CA_SIGNED );
     }
     
     
@@ -330,8 +253,7 @@
         KeyPair keyPair = keyPairGenerator.generateKeyPair();
         
         // Generate the subject's name
-        @SuppressWarnings("restriction")
-        X500Name owner = new X500Name( "apacheds", "directory", "apache", "US" );
+        X500Principal owner = new X500Principal( "CN=apacheds,OU=directory,O=apache,C=US" );
 
         // Create the self-signed certificate
         X509Certificate certificate = CertificateUtil.generateSelfSignedCertificate( owner, keyPair, 365, "SHA256WithECDSA" );
diff --git a/core/src/test/java/org/apache/directory/server/core/security/CertificateUtilTest.java b/core/src/test/java/org/apache/directory/server/core/security/CertificateUtilTest.java
index 2ccc5a5..0a60df2 100644
--- a/core/src/test/java/org/apache/directory/server/core/security/CertificateUtilTest.java
+++ b/core/src/test/java/org/apache/directory/server/core/security/CertificateUtilTest.java
@@ -25,9 +25,9 @@
 import java.security.KeyPairGenerator;
 import java.security.cert.X509Certificate;
 
-import org.junit.Test;
+import javax.security.auth.x500.X500Principal;
 
-import sun.security.x509.X500Name;
+import org.junit.Test;
 
 /**
  * Test for the CertificateUtil class.
@@ -42,7 +42,7 @@
     public void testSelfSignedCertificateCreation() throws IOException, GeneralSecurityException
     {
         // Generate the subject's name
-        X500Name owner = new X500Name( "apacheds", "directory", "apache", "US" );
+        X500Principal owner = new X500Principal( "CN=apacheds,OU=directory,O=apache,C=US" );
         
         
         // generate the asymetric keys
diff --git a/pom.xml b/pom.xml
index a5899bf..f0332c4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1248,6 +1248,12 @@
         <artifactId>bcprov-jdk15on</artifactId>
         <version>${bcprov.version}</version>
       </dependency>
+    
+      <dependency>
+        <groupId>org.bouncycastle</groupId>
+        <artifactId>bcpkix-jdk15on</artifactId>
+        <version>${bcprov.version}</version>
+      </dependency>
 
       <dependency>
         <groupId>org.eclipse.jetty</groupId>
@@ -1260,7 +1266,7 @@
         <artifactId>annotations</artifactId>
         <version>${findbugs.annotations.version}</version>
       </dependency>
-      
+    
       <dependency>
         <groupId>com.github.ben-manes.caffeine</groupId>
         <artifactId>caffeine</artifactId>
diff --git a/service/pom.xml b/service/pom.xml
index 856b264..c438aaf 100644
--- a/service/pom.xml
+++ b/service/pom.xml
@@ -108,10 +108,11 @@
             <configuration>
               <filters>
                 <filter>
-                  <artifact>org.bouncycastle:bcprov-jdk15on</artifact>
+                  <artifact>*:*</artifact>
                   <excludes>
                     <exclude>META-INF/*.SF</exclude>
                     <exclude>META-INF/*.DSA</exclude>
+                    <exclude>META-INF/*.RSA</exclude>
                   </excludes>
                 </filter>
               </filters>
diff --git a/service/src/test/java/org/apache/directory/server/UberJarMainTest.java b/service/src/test/java/org/apache/directory/server/UberJarMainTest.java
index 6de01a5..f6e19d1 100644
--- a/service/src/test/java/org/apache/directory/server/UberJarMainTest.java
+++ b/service/src/test/java/org/apache/directory/server/UberJarMainTest.java
@@ -35,6 +35,8 @@
 import java.util.Locale;
 import java.util.TimeZone;
 
+import javax.security.auth.x500.X500Principal;
+
 import org.apache.directory.api.ldap.codec.api.SchemaBinaryAttributeDetector;
 import org.apache.directory.api.ldap.model.cursor.EntryCursor;
 import org.apache.directory.api.ldap.model.entry.DefaultEntry;
@@ -55,8 +57,6 @@
 import org.junit.Before;
 import org.junit.Test;
 
-import sun.security.x509.X500Name;
-
 import static org.junit.Assert.assertEquals;
 
 
@@ -114,8 +114,7 @@
             KeyPair keyPair = keyPairGenerator.generateKeyPair();
 
             // Generate the subject's name
-            @SuppressWarnings("restriction")
-            X500Name owner = new X500Name( "apacheds", "directory", "apache", "US" );
+            X500Principal owner = new X500Principal( "CN=apacheds,OU=directory,O=apache,C=US" );
 
             // Create the self-signed certificate
             X509Certificate certificate = CertificateUtil.generateSelfSignedCertificate( owner, keyPair, 365,
diff --git a/test-framework/src/main/java/org/apache/directory/server/core/integ/AbstractLdapTestUnit.java b/test-framework/src/main/java/org/apache/directory/server/core/integ/AbstractLdapTestUnit.java
index 9d290c6..b869e8c 100644
--- a/test-framework/src/main/java/org/apache/directory/server/core/integ/AbstractLdapTestUnit.java
+++ b/test-framework/src/main/java/org/apache/directory/server/core/integ/AbstractLdapTestUnit.java
@@ -30,20 +30,19 @@
 import java.security.KeyStore;
 import java.security.cert.X509Certificate;
 
+import javax.security.auth.x500.X500Principal;
+
 import org.apache.directory.server.core.api.DirectoryService;
 import org.apache.directory.server.core.security.CertificateUtil;
 import org.apache.directory.server.kerberos.kdc.KdcServer;
 import org.apache.directory.server.ldap.LdapServer;
 
-import sun.security.x509.X500Name;
-
 
 /**
  * An abstract class created to hold common elements.
  *
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
-@SuppressWarnings("restriction")
 public abstract class AbstractLdapTestUnit
 {
     /** The used DirectoryService instance */
@@ -106,10 +105,10 @@
         KeyPair keyPair = keyPairGenerator.generateKeyPair();
         
         // Generate the subject's name
-        X500Name subject = new X500Name( subjectDn, "directory", "apache", "US" );
+        X500Principal subject = new X500Principal( "CN=" + subjectDn + ",OU=directory,O=apache,C=US" );
         
         // Generate the issuer's name
-        X500Name issuer = new X500Name( issuerDn, "directory", "apache", "US" );
+        X500Principal issuer = new X500Principal( "CN=" + issuerDn + ",OU=directory,O=apache,C=US" );
 
         // Create the self-signed certificate
         X509Certificate certificate = CertificateUtil.generateCertificate( subject, issuer, keyPair, days, algorithm );