Skip to content

Commit

Permalink
Support building using JDK 21 (fixes #662)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcelmay committed Dec 21, 2023
1 parent 6a4bd39 commit f708820
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 47 deletions.
10 changes: 10 additions & 0 deletions greenmail-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,16 @@
<artifactId>hamcrest-library</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
package com.icegreen.greenmail;

import com.icegreen.greenmail.util.DummySSLServerSocketFactory;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.bc.BcX509ExtensionUtils;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import sun.security.x509.*;

import java.io.FileOutputStream;
import java.io.IOException;
Expand All @@ -18,6 +31,23 @@
import static org.assertj.core.api.Assertions.assertThat;

public class DummySSLServerSocketFactoryTest {
@BeforeClass
public static void setUp() {
Security.addProvider(new BouncyCastleProvider());
}

@AfterClass
public static void tearDown() {
Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
}

@After
public void cleanup() {
System.clearProperty(DummySSLServerSocketFactory.GREENMAIL_KEYSTORE_FILE_PROPERTY);
System.clearProperty(DummySSLServerSocketFactory.GREENMAIL_KEYSTORE_PASSWORD_PROPERTY);
System.clearProperty(DummySSLServerSocketFactory.GREENMAIL_KEY_PASSWORD_PROPERTY);
}

@Test
public void testLoadDefaultKeyStore() throws KeyStoreException {
DummySSLServerSocketFactory factory = new DummySSLServerSocketFactory();
Expand All @@ -27,25 +57,18 @@ public void testLoadDefaultKeyStore() throws KeyStoreException {

@Test
public void testLoadKeyStoreViaSystemPropertyWithDefaultKeyPwd()
throws GeneralSecurityException, IOException {
throws GeneralSecurityException, IOException, OperatorCreationException {
testLoadKeyStoreViaSystemProperty("store password", null);
}

@Test
public void testLoadKeyStoreViaSystemPropertyWithProvidedKeyPwd()
throws GeneralSecurityException, IOException {
throws GeneralSecurityException, IOException, OperatorCreationException {
testLoadKeyStoreViaSystemProperty("store password", "key password");
}

@After
public void cleanup() {
System.clearProperty(DummySSLServerSocketFactory.GREENMAIL_KEYSTORE_FILE_PROPERTY);
System.clearProperty(DummySSLServerSocketFactory.GREENMAIL_KEYSTORE_PASSWORD_PROPERTY);
System.clearProperty(DummySSLServerSocketFactory.GREENMAIL_KEY_PASSWORD_PROPERTY);
}

public void testLoadKeyStoreViaSystemProperty(String storePassword, String keyPassword)
throws GeneralSecurityException, IOException {
throws GeneralSecurityException, IOException, OperatorCreationException {
// Prepare new keystore
KeyStore testKs = KeyStore.getInstance(KeyStore.getDefaultType());
testKs.load(null, null); // Initialize
Expand All @@ -56,7 +79,7 @@ public void testLoadKeyStoreViaSystemProperty(String storePassword, String keyPa
KeyPair keyPair = keyPairGenerator.generateKeyPair();

String dn = "CN=greenmail.test";
final X509Certificate cert = generateCertificate(dn, keyPair, 1, AlgorithmId.get("SHA256WithRSA"));
final X509Certificate cert = generateCertificate(dn, keyPair, 1, "SHA256WithRSA");
final String alias = "test-key";
testKs.setKeyEntry(alias, keyPair.getPrivate(),
(null != keyPassword ? keyPassword : storePassword).toCharArray(),
Expand Down Expand Up @@ -88,44 +111,36 @@ public void testLoadKeyStoreViaSystemProperty(String storePassword, String keyPa
/**
* Create a self-signed X.509 certificate
* <p>
* Based on <a href="https://stackoverflow.com/questions/1615871">https://stackoverflow.com/questions/1615871</a>
* Based on <a href="https://stackoverflow.com/questions/29852290/self-signed-x509-certificate-with-bouncy-castle-in-java">https://stackoverflow.com/questions/29852290/self-signed-x509-certificate-with-bouncy-castle-in-java</a>
*
* @param dn the X.509 Distinguished Name
* @param pair the KeyPair
* @param days how many days till expiration
* @param algo the signing algorithm, eg "SHA256WithRSA"
* @param dn the X.509 Distinguished Name
* @param pair the KeyPair
* @param days how many days till expiration
* @param signingAlgorithm the signing algorithm, e.g. "SHA256WithRSA"
*/
public X509Certificate generateCertificate(String dn, KeyPair pair, int days, AlgorithmId algo)
throws GeneralSecurityException, IOException {
PrivateKey privateKey = pair.getPrivate();
X509CertInfo info = new X509CertInfo();

public X509Certificate generateCertificate(String dn, KeyPair pair, int days, String signingAlgorithm)
throws GeneralSecurityException, IOException, OperatorCreationException {
Instant now = Instant.now();
CertificateValidity interval = new CertificateValidity(
X500Name issuer = new X500Name(dn);

final BcX509ExtensionUtils bcX509ExtensionUtils = new BcX509ExtensionUtils();
final SubjectPublicKeyInfo subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(pair.getPublic().getEncoded());
final X509v3CertificateBuilder x509v3CertificateBuilder = new JcaX509v3CertificateBuilder(issuer,
new BigInteger(64, new SecureRandom()),
Date.from(now),
Date.from(now.plus(Duration.ofDays(days)))
);

X500Name owner = new X500Name(dn);

info.set(X509CertInfo.VALIDITY, interval);
info.set(X509CertInfo.SERIAL_NUMBER,
new CertificateSerialNumber(new BigInteger(64, new SecureRandom())));
info.set(X509CertInfo.SUBJECT, owner);
info.set(X509CertInfo.ISSUER, owner);
info.set(X509CertInfo.KEY, new CertificateX509Key(pair.getPublic()));
info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo));

// Sign the cert to identify the algorithm that's used.
X509CertImpl cert = new X509CertImpl(info);
cert.sign(privateKey, algo.getName());

// Update the algorithm, and resign.
algo = (AlgorithmId) cert.get(X509CertImpl.SIG_ALG);
info.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, algo);
cert = new X509CertImpl(info);
cert.sign(privateKey, algo.getName());
return cert;
Date.from(now.plus(Duration.ofDays(days))),
issuer,
pair.getPublic())
.addExtension(Extension.basicConstraints, true, new BasicConstraints(true))
.addExtension(Extension.subjectKeyIdentifier, false,
bcX509ExtensionUtils.createSubjectKeyIdentifier(subjectPublicKeyInfo))
.addExtension(Extension.authorityKeyIdentifier, false,
bcX509ExtensionUtils.createAuthorityKeyIdentifier(subjectPublicKeyInfo));

final X509CertificateHolder certificateHolder = x509v3CertificateBuilder.build(
new JcaContentSignerBuilder(signingAlgorithm).build(pair.getPrivate()));
return new JcaX509CertificateConverter()
.setProvider(BouncyCastleProvider.PROVIDER_NAME)
.getCertificate(certificateHolder);
}
}
11 changes: 11 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,17 @@
<artifactId>jetty-util</artifactId>
<version>${jetty.version}</version>
</dependency>
<!-- Testing TLS -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.77</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
<version>1.77</version>
</dependency>
</dependencies>
</dependencyManagement>
<profiles>
Expand Down

0 comments on commit f708820

Please sign in to comment.