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 );