blob: d724253ae0dac400e848e7e2ddfc81b1309bf8db [file]
<%#
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 <%= polygene.packageName %>.app;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.PublicKey;
import java.security.Security;
import java.security.UnrecoverableKeyException;
import java.security.cert.X509Certificate;
import java.util.Date;
import javax.net.ssl.KeyManagerFactory;
import javax.security.auth.x500.X500Principal;
import org.apache.polygene.bootstrap.AssemblyException;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.X509KeyUsage;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
import static <%= polygene.packageName %>.bootstrap.connectivity.HttpServerModule.*;
/**
* This class creates self-signed SSL certificate into the security/ directory.
* <p>
* NOTE: This is for use in development only. For production setup, please use better tooling.
* </p>
*/
class DevelopmentKeyManagement
{
private static final String COMMON_NAME = "<%= polygene.packageName %>";
private static final int RSA_KEY_LEN = 4096;
static void initializeKeyStore()
{
@SuppressWarnings( "unused" )
KeyStore trustStore = loadKeyStore( new File(TRUSTSTORE_FILENAME), TRUSTSTORE_TYPE, KS_PASSWORD );
File keyFile = new File( SERVER_KEYSTORE_FILENAME );
KeyStore keyStore = loadKeyStore( keyFile, SERVER_KEYSTORE_TYPE, KS_PASSWORD );
initializeKeyManager( keyStore, KS_PASSWORD );
createKeyStoreData( keyStore, keyFile, SERVER_KEYSTORE_TYPE, KS_PASSWORD );
}
private static KeyStore loadKeyStore( File keyFile, String type, String password )
{
if( !keyFile.exists() )
{
createKeyStore(keyFile, type, password);
}
try( FileInputStream fis = new FileInputStream( keyFile ) )
{
KeyStore ks = KeyStore.getInstance( type );
char[] pwd = password.toCharArray();
ks.load( fis, pwd );
return ks;
}
catch( Exception e )
{
throw new AssemblyException( "Unable to create keystore.", e );
}
}
@SuppressWarnings( "ResultOfMethodCallIgnored" )
private static void createKeyStore( File keyFile, String type, String password )
throws AssemblyException
{
if( !keyFile.getParentFile().exists() )
{
keyFile.getParentFile().mkdirs();
}
try( FileOutputStream fos = new FileOutputStream( keyFile ) )
{
KeyStore ks = KeyStore.getInstance( type );
char[] pwd = password.toCharArray();
ks.load( null, pwd );
ks.store( fos, pwd );
}
catch( Exception e )
{
throw new AssemblyException( "Unable to create keystore.", e );
}
}
private static void initializeKeyManager( KeyStore keyStore, String password )
{
try
{
KeyManagerFactory kmf = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm() );
kmf.init( keyStore, password.toCharArray() );
}
catch( NoSuchAlgorithmException | KeyStoreException | UnrecoverableKeyException e )
{
throw new AssemblyException( "Unable to initialize KeyManager.", e );
}
}
private static void createKeyStoreData( KeyStore keyStore, File keyFile, String type, String password )
{
try
{
if( keyStore.getCertificate( COMMON_NAME ) != null )
{
return;
}
KeyPair keyPair = generateKeyPair();
X509Certificate certificate = selfSignedCertificate( keyPair, COMMON_NAME, 30 );
keyStore.setCertificateEntry( COMMON_NAME, certificate );
storeKeyStore(keyStore, keyFile, password, type);
System.out.println("Created Self-signed certificated:");
System.out.println(convertCertificateToPEM( certificate ));
}
catch( KeyStoreException e )
{
throw new AssemblyException( "Unable to store certificate in keystore.", e );
}
}
private static void storeKeyStore( KeyStore keyStore, File keyFile, String password, String type )
{
try( FileOutputStream fos = new FileOutputStream( keyFile ) )
{
KeyStore ks = KeyStore.getInstance( type );
char[] pwd = password.toCharArray();
ks.load( null, pwd );
ks.store( fos, pwd );
}
catch( Exception e )
{
throw new AssemblyException( "Unable to create keystore.", e );
}
}
private static KeyPair generateKeyPair()
{
try
{
KeyPairGenerator keyGen = KeyPairGenerator.getInstance( "RSA" );
keyGen.initialize( RSA_KEY_LEN );
return keyGen.generateKeyPair();
}
catch( NoSuchAlgorithmException e )
{
throw new AssemblyException( "RSA encryption is not available on this system.", e );
}
}
@SuppressWarnings( "unused" )
private static PKCS10CertificationRequest generateCSR( KeyPair pair, String subjectDN )
{
try
{
X500Principal subject = new X500Principal( "CN=" + subjectDN );
PublicKey publicKey = pair.getPublic();
PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder( subject, publicKey );
JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder( "SHA256withRSA" );
ContentSigner signer = csBuilder.build( pair.getPrivate() );
return p10Builder.build( signer );
}
catch( Exception e )
{
throw new AssemblyException( "Unable to generate Certificate Signing Request", e );
}
}
private static String convertCertificateToPEM( X509Certificate signedCertificate )
{
try( StringWriter signedCertificatePEMDataStringWriter = new StringWriter() )
{
JcaPEMWriter pemWriter = new JcaPEMWriter( signedCertificatePEMDataStringWriter );
pemWriter.writeObject( signedCertificate );
pemWriter.close();
return signedCertificatePEMDataStringWriter.toString();
}
catch( IOException e )
{
throw new AssemblyException( "Unable to convert certificate to PEM string.", e );
}
}
private static X509Certificate selfSignedCertificate( KeyPair keyPair, String subjectDN, @SuppressWarnings( "SameParameterValue" ) int days )
{
try
{
Provider bcProvider = new BouncyCastleProvider();
Security.addProvider( bcProvider );
long now = System.currentTimeMillis();
Date startDate = new Date( now );
Date endDate = new Date( now + days * 25 * 3_600_000L );
X500Name dnName = new X500Name( "CN=" + subjectDN );
BigInteger certSerialNumber = new BigInteger( Long.toString( now ) );
String signatureAlgorithm = "SHA256WithRSA"; // <-- Use appropriate signature algorithm based on your keyPair algorithm.
ContentSigner contentSigner = new JcaContentSignerBuilder( signatureAlgorithm ).build( keyPair.getPrivate() );
JcaX509v3CertificateBuilder certBuilder =
new JcaX509v3CertificateBuilder( dnName, certSerialNumber, startDate, endDate, dnName, keyPair.getPublic() );
// Basic Constraints
certBuilder.addExtension(
new ASN1ObjectIdentifier( "2.5.29.19" ),
true,
new BasicConstraints( true ) ); // Basic Constraints is usually marked as critical.
// Key Usage constraints
certBuilder.addExtension(
new ASN1ObjectIdentifier( "2.5.29.15" ),
true,
new X509KeyUsage(
X509KeyUsage.digitalSignature |
X509KeyUsage.keyCertSign |
X509KeyUsage.nonRepudiation |
X509KeyUsage.keyEncipherment |
X509KeyUsage.dataEncipherment ) );
return new JcaX509CertificateConverter().setProvider( bcProvider ).getCertificate( certBuilder.build( contentSigner ) );
}
catch( Exception e )
{
throw new AssemblyException( "Unable to self-sign certificate.", e );
}
}
}