TL;DR
This guide shows you how to replicate OpenSSL’s openssl verify functionality using the Bouncy Castle cryptography library in Java. This is useful when you need a pure-Java solution for certificate verification without relying on native OpenSSL libraries.
Prerequisites
- Java Development Kit (JDK) installed
- Bouncy Castle Provider JAR file added to your project’s classpath. Download from the Bouncy Castle website.
Steps
- Add the Bouncy Castle Provider
- Load the Certificate
- Load the Trusted Root Certificates
- Create a Trust Manager
- Create an SSLContext
- Verify the Certificate
- Handle Exceptions
First, you need to register the Bouncy Castle provider in your Java application. This tells Java to use Bouncy Castle’s cryptographic algorithms.
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
Load the certificate you want to verify from a file (e.g., PEM or DER format). Use a java.io.FileInputStream and a java.security.cert.CertificateFactory.
FileInputStream fis = new FileInputStream("certificate.pem");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate) cf.load(fis);
fis.close();
You’ll need to load the trusted root certificates that form your trust chain. This is often a CA bundle file.
FileInputStream trustFis = new FileInputStream("truststore.pem");
CertificateFactory trustCf = CertificateFactory.getInstance("X.509");
List<X509Certificate> trustedCerts = (List<X509Certificate>>) trustCf.load(trustFis);
trustFis.close();
Implement a custom TrustManager to handle the trusted certificates.
class CustomTrustManager implements TrustManager {
private final List<X509Certificate> trustedCerts;
public CustomTrustManager(List<X509Certificate> trustedCerts) {
this.trustedCerts = trustedCerts;
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// Implement your trust logic here.
for (X509Certificate cert : chain) {
if (!trustedCerts.contains(cert)) {
throw new CertificateException("Untrusted certificate");
}
}
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// Not used in this example.
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return trustedCerts.toArray(new X509Certificate[0]);
}
}
Create an SSLContext using the custom TrustManager.
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{new CustomTrustManager(trustedCerts)}, null);
This step is where you actually verify the certificate against the trusted root certificates.
X509CertChain chain = new X509CertChain(new java.util.ArrayList<X509Certificate>(java.util.Arrays.asList(certificate)));
chain.validateAll(null, null);
If the validation succeeds without throwing an exception, the certificate is considered valid.
Catch CertificateException and other relevant exceptions to handle invalid certificates or trust chain issues. These exceptions indicate verification failures.