/* * Copyright (c) 2002-2017 "Neo Technology," * Network Engine for Objects in Lund AB [http://neotechnology.com] * * This file is part of Neo4j. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.neo4j.driver.v1.util; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.cert.X509CertificateHolder; 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; import org.bouncycastle.pkcs.PKCS10CertificationRequest; import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder; import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; import org.bouncycastle.util.io.pem.PemObject; import org.bouncycastle.util.io.pem.PemWriter; import org.junit.Test; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.math.BigInteger; import java.security.GeneralSecurityException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; import java.security.Security; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.Date; import java.util.Enumeration; import javax.security.auth.x500.X500Principal; import org.neo4j.driver.internal.util.CertificateTool; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.neo4j.driver.internal.util.CertificateTool.saveX509Cert; public class CertificateToolTest { static { // adds the Bouncy castle provider to java security Security.addProvider( new BouncyCastleProvider() ); } public static KeyPair generateKeyPair() throws NoSuchProviderException, NoSuchAlgorithmException { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance( "RSA", "BC" ); keyPairGenerator.initialize( 2048, new SecureRandom() ); KeyPair keyPair = keyPairGenerator.generateKeyPair(); return keyPair; } public static X509Certificate generateCert( X500Name issuer, X500Name subject, KeyPair issuerKeys, PublicKey publicKey ) throws GeneralSecurityException, IOException, OperatorCreationException { // Create x509 certificate Date startDate = new Date( System.currentTimeMillis() ); Date endDate = new Date( System.currentTimeMillis() + 365L * 24L * 60L * 60L * 1000L ); BigInteger serialNum = BigInteger.valueOf( System.currentTimeMillis() ); X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder( issuer, serialNum, startDate, endDate, subject, publicKey ); // Get the certificate back ContentSigner signer = new JcaContentSignerBuilder( "SHA512WithRSAEncryption" ).build( issuerKeys.getPrivate() ); X509CertificateHolder certHolder = certBuilder.build( signer ); X509Certificate certificate = new JcaX509CertificateConverter().setProvider( "BC" ) .getCertificate( certHolder ); certificate.verify( issuerKeys.getPublic() ); return certificate; } public static class SelfSignedCertificateGenerator { private final KeyPair keyPair; private final X509Certificate certificate; public SelfSignedCertificateGenerator() throws GeneralSecurityException, IOException, OperatorCreationException { // Create the public/private rsa key pair keyPair = generateKeyPair(); // Create x509 certificate certificate = generateCert( new X500Name( "CN=NEO4J_JAVA_DRIVER_TEST_ROOT" ), new X500Name( "CN=NEO4J_JAVA_DRIVER_TEST_ROOT" ), keyPair, keyPair.getPublic() ); } public void savePrivateKey( File saveTo ) throws IOException { writePem( "PRIVATE KEY", keyPair.getPrivate().getEncoded(), saveTo ); } public void saveSelfSignedCertificate( File saveTo ) throws CertificateEncodingException, IOException { writePem( "CERTIFICATE", certificate.getEncoded(), saveTo ); } public X509Certificate sign(PKCS10CertificationRequest csr, PublicKey csrPublicKey ) throws GeneralSecurityException, IOException, OperatorCreationException { X509Certificate certificate = generateCert( X500Name.getInstance( this.certificate.getSubjectX500Principal().getEncoded() ), csr.getSubject(), keyPair, csrPublicKey ); return certificate; } } public static class CertificateSigningRequestGenerator { // ref: http://senthadev.com/generating-csr-using-java-and-bouncycastle-api.html private final KeyPair keyPair; private final PKCS10CertificationRequest csr; public CertificateSigningRequestGenerator() throws NoSuchAlgorithmException, OperatorCreationException { KeyPairGenerator gen = KeyPairGenerator.getInstance( "RSA" ); gen.initialize( 2048, new SecureRandom() ); keyPair = gen.generateKeyPair(); X500Principal subject = new X500Principal( "CN=NEO4j_JAVA_DRIVER_TEST_SERVER" ); ContentSigner signGen = new JcaContentSignerBuilder( "SHA512WithRSAEncryption" ).build( keyPair.getPrivate() ); PKCS10CertificationRequestBuilder builder = new JcaPKCS10CertificationRequestBuilder( subject, keyPair.getPublic() ); csr = builder.build( signGen ); } public PrivateKey privateKey() { return keyPair.getPrivate(); } public PublicKey publicKey() { return keyPair.getPublic(); } public PKCS10CertificationRequest certificateSigningRequest() { return csr; } public void savePrivateKey( File saveTo ) throws IOException { writePem( "PRIVATE KEY", keyPair.getPrivate().getEncoded(), saveTo ); } } /** * Create a random certificate * * @return a random certificate * @throws GeneralSecurityException, IOException, OperatorCreationException */ public static X509Certificate generateSelfSignedCertificate() throws GeneralSecurityException, IOException, OperatorCreationException { return new SelfSignedCertificateGenerator().certificate; } private static void writePem( String type, byte[] encodedContent, File path ) throws IOException { if( path.getParentFile() != null && path.getParentFile().exists() ) path.getParentFile().mkdirs(); try ( PemWriter writer = new PemWriter( new FileWriter( path ) ) ) { writer.writeObject( new PemObject( type, encodedContent ) ); writer.flush(); } } @Test public void shouldLoadMultipleCertsIntoKeyStore() throws Throwable { // Given File certFile = File.createTempFile( "3random", ".cer" ); certFile.deleteOnExit(); X509Certificate cert1 = generateSelfSignedCertificate(); X509Certificate cert2 = generateSelfSignedCertificate(); X509Certificate cert3 = generateSelfSignedCertificate(); saveX509Cert( new Certificate[] {cert1, cert2, cert3}, certFile ); KeyStore keyStore = KeyStore.getInstance( "JKS" ); keyStore.load( null, null ); // When CertificateTool.loadX509Cert( certFile, keyStore ); // Then Enumeration<String> aliases = keyStore.aliases(); assertTrue( aliases.hasMoreElements() ); assertTrue( aliases.nextElement().startsWith( "neo4j.javadriver.trustedcert" ) ); assertTrue( aliases.hasMoreElements() ); assertTrue( aliases.nextElement().startsWith( "neo4j.javadriver.trustedcert" ) ); assertTrue( aliases.hasMoreElements() ); assertTrue( aliases.nextElement().startsWith( "neo4j.javadriver.trustedcert" ) ); assertFalse( aliases.hasMoreElements() ); } }