Search code examples
javaspringsslspring-bootbouncycastle

Bouncy castle with spring boot


I am trying to use Https over http, I generated the CA certificate with bouncy castle and generated from the .cer file the jks file, spring boot started successfully with the properties provided with an embedded tomcat.

The problem is that I'm getting always insecure ssl certificate from the browser (for example from chrome : This CA Root certificate is not trusted because it is not in the Trusted Root Certification Authorities store.) while I add the trust store programatically with another config class.

Here is my code:

import org.apache.commons.codec.binary.Base64; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.cmp.CertRepMessage; import org.bouncycastle.asn1.cmp.PKIBody; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.cert.CertException; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.X509v3CertificateBuilder; import org.bouncycastle.cert.cmp.CMPException; import org.bouncycastle.cert.cmp.ProtectedPKIMessage; import org.bouncycastle.cert.cmp.ProtectedPKIMessageBuilder; 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.ContentVerifierProvider; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;

import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.math.BigInteger; import java.security.*; import java.security.cert.X509Certificate; import java.util.Date;

public class generateService {

    private static final String BC = BouncyCastleProvider.PROVIDER_NAME;


    private static X509CertificateHolder makeV3Certificate(KeyPair subKP, String _subDN, KeyPair issKP, String _issDN)
            throws GeneralSecurityException, IOException, OperatorCreationException, CertException
    {

        PublicKey subPub  = subKP.getPublic();
        PrivateKey issPriv = issKP.getPrivate();
        PublicKey  issPub  = issKP.getPublic();

        X509v3CertificateBuilder v1CertGen = new JcaX509v3CertificateBuilder(
                new X500Name(_issDN),
                BigInteger.valueOf(System.currentTimeMillis()),
                new Date(System.currentTimeMillis()),
                new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 100)),
                new X500Name(_subDN),
                subPub);

        ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSA").setProvider(BC).build(issPriv);

        X509CertificateHolder certHolder = v1CertGen.build(signer);

        ContentVerifierProvider verifier = new JcaContentVerifierProviderBuilder().setProvider(BC).build(issPub);
        return certHolder;
    }


    public X509Certificate test(KeyPair kp) throws OperatorCreationException, GeneralSecurityException, CertException, IOException, CMPException {

    Provider bcProvider = new BouncyCastleProvider();
    Security.addProvider(bcProvider);
    X509CertificateHolder cert = makeV3Certificate(kp, "CN=CA", kp, "CN=CA");
    GeneralName sender = new GeneralName(new X500Name("CN=CA"));
    GeneralName recipient = new GeneralName(new X500Name("CN=CA"));

    ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSAEncryption").setProvider(BC).build(kp.getPrivate());
    ProtectedPKIMessage message = new ProtectedPKIMessageBuilder(sender, recipient)
            .setBody(new PKIBody(PKIBody.TYPE_INIT_REP, CertRepMessage.getInstance(new DERSequence(new DERSequence()))))
            .addCMPCertificate(cert)
            .build(signer);

    X509Certificate jcaCert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(message.getCertificates()[0]);
    return jcaCert;
    }




    public void generateJKS() throws GeneralSecurityException, IOException, OperatorCreationException, CMPException, CertException {
        Provider bcProvider = new BouncyCastleProvider();
        Security.addProvider(bcProvider);
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(1024);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        X509Certificate certificate = test(keyPair);

        final FileOutputStream os = new FileOutputStream("E:\\cert4.cer");
        os.write("-----BEGIN CERTIFICATE-----\n".getBytes("US-ASCII"));
        os.write(Base64.encodeBase64(certificate.getEncoded(), true));
        os.write("-----END CERTIFICATE-----\n".getBytes("US-ASCII"));
        os.close();

        KeyStore keyStore = KeyStore.getInstance("JKS");
        keyStore.load(null,null);
        X509Certificate[] certChain = new X509Certificate[1];
        certChain[0] = certificate;
        keyStore.setKeyEntry("myaliaskey", (Key)keyPair.getPrivate(), "secret".toCharArray(), certChain);
        OutputStream outputStream = new FileOutputStream("E:\\keystoreRSA4.jks");
        keyStore.store(outputStream, "secret".toCharArray());
        outputStream.flush();
        outputStream.close();

    }



    public static void main(String[] args) throws Exception {

        new generateService().generateJKS();
    } }

Application.properties:

server.port = 8443 server.http.port = 8080 server.ssl.key-store =  E:\\keystoreRSA4.jks server.ssl.key-password = secret server.ssl.keyAlias = myaliaskey

/***************************************************************************/

import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;

@Configuration public class SSLConfig {


    @PostConstruct
    private void configureSSL() {
        System.setProperty("https.protocols", "TLSv1.2");
        System.setProperty("javax.net.ssl.trustStore", "E:\\keystoreRSA4.jks");
        System.setProperty("javax.net.ssl.keyStore", "E:\\keystoreRSA4.jks");
        System.setProperty("javax.net.ssl.keyStorePassword", "secret");

    } }


import org.apache.catalina.Context; import org.apache.catalina.connector.Connector; import org.apache.tomcat.util.descriptor.web.SecurityCollection; import org.apache.tomcat.util.descriptor.web.SecurityConstraint; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;

@Configuration public class TomcatConfig {
    @Value("${server.http.port}")
    private int httpPort;

    @Value("${server.port}")
    private int httpsPort;

    @Bean
    public EmbeddedServletContainerFactory servletContainer() {
        TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory() {
            @Override
            protected void postProcessContext(Context context) {
                SecurityConstraint securityConstraint = new SecurityConstraint();
                securityConstraint.setUserConstraint("CONFIDENTIAL");
                SecurityCollection collection = new SecurityCollection();
                collection.addPattern("/*");
                securityConstraint.addCollection(collection);
                context.addConstraint(securityConstraint);
            }
        };
        tomcat.addAdditionalTomcatConnectors(initiateHttpConnector());
        return tomcat;
    }

    public Connector initiateHttpConnector() {
        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
        connector.setScheme("https");
        connector.setPort(httpPort);
        connector.setSecure(true);
        connector.setRedirectPort(httpsPort); 
        return connector;
    } }

Any help will be appreciated and thank you in advance.


Solution

  • how i can figure it to let the browser accept it as trust certificate?

    Fortunately, you can't. There's a discussion about the topic in this question:

    If we simply said "HTTPS is now possible with self-signed certificates", your browser could not distinguish whether a site you are trying to visit has a self-signed cert because it is supposed to, or because you are being attacked. Thus, it would decrease security.

    You have to add an exception to your browser to accept your certificate. Normally during development. For production use, you have to buy a certificate from a trusted vendor like Verisign.

    To add an exception to your browser, please see this other question.

    Cheers!