I have my file, key.key which i now used OpenSSL to convert to PKCS8 format, this is the key template
-----BEGIN PRIVATE KEY-----
some letters and numbers
-----END PRIVATE KEY-----
When im trying to load it into my key store as so:
PrivateKey privateKey = kf.generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
I get the Error
com.android.org.conscrypt.OpenSSLX509CertificateFactory$ParsingException: Error parsing private key
(im using OkHttpChannel builder and Conscrypt as a security provider).
I tried changing the key's format, which is why it is now in PKCS8 since iv'e read its the eaziest one for android java to read.
Update::
Even after loading the key and the two certificates i still get the
------------------Untrusted chain: ---------------------- error, any help ?
The code used:
private static byte[] getBytesFromInputStream(InputStream is) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
byte[] buffer = new byte[0xFFFF];
for (int len = is.read(buffer); len != -1; len = is.read(buffer)) {
os.write(buffer, 0, len);
}
return os.toByteArray();
}
// build a key store from a set of raw certificates
private static KeyStore createKeyStore(Resources resources, int certsId, boolean ca) {
KeyStore ks = null;
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
if (ca) {
ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null);
InputStream certIS = resources.openRawResource(R.raw.ca_crt);
X509Certificate cert = (X509Certificate) cf.generateCertificate(certIS);
ks.setCertificateEntry("ca", cert);
} else {
ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null, null);
InputStream certIS = resources.openRawResource(R.raw.client_crt);
Certificate cert = cf.generateCertificate(certIS);
InputStream privateKeyIS = resources.openRawResource(R.raw.client_pkcs8);
byte[] privateKeyBytes = RNGrpcChannelBuilder.getBytesFromInputStream(privateKeyIS);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey privateKey = kf.generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
Certificate[] certChain = new Certificate[1];
certChain[0] = cert;
ks.setKeyEntry("client", privateKey, null, certChain);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return ks;
}
public static ManagedChannel build(String host, int port, Resources resources,
#Nullable String serverHostOverride) {
KeyStore ca = RNGrpcChannelBuilder.createKeyStore(resources, R.array.certs, true);
KeyStore client = RNGrpcChannelBuilder.createKeyStore(resources, R.array.certs, false);
OkHttpChannelBuilder channelBuilder =
OkHttpChannelBuilder.forAddress(host, port);
if (serverHostOverride != null) {
// Force the hostname to match the cert the server uses.
channelBuilder.overrideAuthority(serverHostOverride);
}
try {
channelBuilder.negotiationType(io.grpc.okhttp.NegotiationType.TLS);
channelBuilder.useTransportSecurity();
channelBuilder.sslSocketFactory(getSslSocketFactory(ca, client));
} catch (Exception e) {
throw new RuntimeException(e);
}
return channelBuilder.build();
}
private static SSLSocketFactory getSslSocketFactory(KeyStore ca, KeyStore client)
throws Exception {
KeyManagerFactory kmf =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
String password = "";
kmf.init(client, password.toCharArray());
// initialize trust manager factor from certs keystore
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ca);
// initialize SSL context from trust manager factory
SSLContext context = SSLContext.getInstance("TLS");
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
// return socket factory from the SSL context
return context.getSocketFactory();
}
Related
I have a working stunnel setup and am trying to use the same pem file containing the client certificate and client key and the server trust X509Certificate in a .crt file.
I consistently get this exception when it tries to write the first heartbeat packet.
javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:353)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:296)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:291)
at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:654)
at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.onCertificate(CertificateMessage.java:473)
at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.consume(CertificateMessage.java:369)
at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392)
at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:443)
at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:1074)
at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask$DelegatedAction.run(SSLEngineImpl.java:1061)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/sun.security.ssl.SSLEngineImpl$DelegatedTask.run(SSLEngineImpl.java:1008)
at tlschannel.impl.TlsChannelImpl.handleTask(TlsChannelImpl.java:232)
at tlschannel.impl.TlsChannelImpl.handshakeLoop(TlsChannelImpl.java:570)
at tlschannel.impl.TlsChannelImpl.writeAndHandshake(TlsChannelImpl.java:540)
at tlschannel.impl.TlsChannelImpl.doHandshake(TlsChannelImpl.java:511)
at tlschannel.impl.TlsChannelImpl.handshake(TlsChannelImpl.java:493)
at tlschannel.impl.TlsChannelImpl.write(TlsChannelImpl.java:362)
at tlschannel.ClientTlsChannel.write(ClientTlsChannel.java:164)
at tlschannel.ClientTlsChannel.write(ClientTlsChannel.java:169)
at tlschannel.ClientTlsChannel.write(ClientTlsChannel.java:174)
Note I am certain this is the same .crt file used by stunnel successfully.
Here is the code that is used to construct the SSLContext with trust and client certificate and key.
// initialize the SSLContext, a configuration holder, reusable object
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
// Get the location of the key and cert file and read it in
HashMap<String, List<?>> keys = this.readPem(pemPath);
// initialize the key and trust managers
PrivateKey key = (PrivateKey) keys.get("keys").get(0);
List<X509Certificate> certs = (List<X509Certificate>)keys.get("certs");
X509Certificate[] certsArray = new X509Certificate[certs.size()];
certs.toArray(certsArray);
// We use the pemPath as the keystore alias for this key and certs
KeyManager[] km = createKeyStore(key, certsArray, pemPath);
TrustManager[] tm = createTrustStore(trustPath);
sslContext.init(km, tm, null);
final InetSocketAddress isa = new InetSocketAddress(InetAddress.getByName(host), port);
socketChannel = SocketChannel.open();
socketChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
socketChannel.connect(isa);
socketChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
socketChannel.configureBlocking(true);
// create TlsChannel builder, combining the raw channel
// and the SSLEngine, using minimal options
ClientTlsChannel.Builder builder = ClientTlsChannel.newBuilder(socketChannel, sslContext);
tlsChannel = builder.build();
Here is the readPem method:
private HashMap<String, List<?>> readPem(String filePath) throws FileNotFoundException, IOException,
CertificateException, KeyStoreException, NoSuchAlgorithmException, InvalidKeySpecException {
HashMap<String, List<?>> keys = new HashMap<String, List<?>>();
File file = new File(filePath);
try (Reader reader = new InputStreamReader(new FileInputStream(file), UTF_8)) {
StringBuilder stringBuilder = new StringBuilder();
CharBuffer buffer = CharBuffer.allocate(2048);
while (reader.read(buffer) != -1) {
buffer.flip();
stringBuilder.append(buffer);
buffer.clear();
}
Matcher certMatcher = CERT_PATTERN.matcher(stringBuilder);
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
List<X509Certificate> certificates = new ArrayList<>();
int start = 0;
while (certMatcher.find(start)) {
byte[] cert_buffer = Base64.getMimeDecoder().decode(certMatcher.group(1).getBytes(UTF_8));
certificates.add((X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(cert_buffer)));
start = certMatcher.end();
}
keys.put("certs", certificates);
Matcher keyMatcher = KEY_PATTERN.matcher(stringBuilder);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
if (!keyMatcher.find()) {
throw new KeyStoreException("found no private key: " + keyMatcher);
}
byte[] encodedKey = Base64.getMimeDecoder().decode(keyMatcher.group(1).getBytes(UTF_8));
List<PrivateKey> keyList = new ArrayList<>();
PKCS8EncodedKeySpec eKey = new PKCS8EncodedKeySpec(encodedKey);
PrivateKey pKey = keyFactory.generatePrivate(eKey);
keyList.add(pKey);
keys.put("keys", keyList);
}
return keys;
}
Here are the 2 methods for creating the keystore and truststore:
private TrustManager[] createTrustStore(String trustPath) throws KeyStoreException,
NoSuchAlgorithmException, CertificateException,
IOException {
InputStream fis = new FileInputStream(new File(trustPath));
KeyStore ksTrust = KeyStore.getInstance("jks");
ksTrust.load(null);
BufferedInputStream bis = new BufferedInputStream(fis);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
while (bis.available() > 0) {
X509Certificate cert = (X509Certificate) cf.generateCertificate(bis);
ksTrust.setCertificateEntry("tradepoint"+bis.available(), cert);
}
// TrustManagers decide whether to allow connections
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
tmf.init(ksTrust);
return tmf.getTrustManagers();
}
/**
* Create a KeyStore from standard PEM files
*
* #param key the private key from the PEM file
* #param certs the certificate(s) from the PEM file
* #param name the alias used in the keystore
*/
public static KeyManager[] createKeyStore(PrivateKey key, X509Certificate[] certs, String name)
throws Exception, KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
final KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(null);
// Import private key
char[] pwdChar = "password".toCharArray();
keystore.setKeyEntry(name, key, pwdChar, certs);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keystore, pwdChar);
final KeyManager[] km = kmf.getKeyManagers();
return km;
}
Does anyone have an idea what might cause this exception given it is a correct certificate?
I've already saw this question: Need to do a GET&POST HTTPS Request using a .cer certificate
Mine is quite different:
It is possible to make an HTTPS request using Java (vanilla, or using any library), trusting a server certificate and providing a client certificate, without using a keystore but using plain certificates?
I have both certs in X.509 format, and I don't want to have every certificate in a keystore.
This is a rough example. Represents the X509KeyManager decorator.
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(null, null);
X509KeyManager manager = (X509KeyManager) kmf.getKeyManagers()[0];
KeyManager km = new X509KeyManager() {
#Override
public String[] getClientAliases(String s, Principal[] principals) {
return manager.getServerAliases(s, principals);
}
#Override
public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) {
return manager.chooseClientAlias(strings, principals, socket);
}
#Override
public String[] getServerAliases(String s, Principal[] principals) {
return manager.getServerAliases(s, principals);
}
#Override
public String chooseServerAlias(String s, Principal[] principals, Socket socket) {
return manager.chooseServerAlias(s, principals, socket);
}
#Override
public X509Certificate[] getCertificateChain(String s) {
// You can use `s` to select the appropriate file
try {
File file = new File("path to certificate");
try(InputStream is = new FileInputStream(file)) {
CertificateFactory factory = CertificateFactory.getInstance("X.509");
return new X509Certificate[] {
(X509Certificate) factory.generateCertificate(is)
};
}
}
catch (CertificateException| IOException e) {
e.printStackTrace();
}
return null;
}
#Override
public PrivateKey getPrivateKey(String s) {
// You can use `s` to select the appropriate file
// load and private key from selected certificate
// this use for certificate authorisation
try {
File file = new File("private key file");
byte buffer[] = Files.readAllBytes(file.toPath());
KeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
KeyFactory factory = KeyFactory.getInstance("RSA");
return factory.generatePrivate(keySpec);
}
catch (NoSuchAlgorithmException | IOException | InvalidKeySpecException e) {
e.printStackTrace();
}
return null;
}
};
TrustManager tm = new X509TrustManager() {
#Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
#Override
public X509Certificate[] getAcceptedIssuers() {
try {
File file = new File("path to certificate");
try(InputStream is = new FileInputStream(file)) {
CertificateFactory factory = CertificateFactory.getInstance("X.509");
return new X509Certificate[] {
(X509Certificate) factory.generateCertificate(is)
};
}
}
catch (CertificateException| IOException e) {
e.printStackTrace();
}
return null;
}
};
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init((KeyStore)null); //use java system trust certificates
TrustManager managers[] = new TrustManager[tmf.getTrustManagers().length + 1];
System.arraycopy(tmf.getTrustManagers(), 0, managers, 0, tmf.getTrustManagers().length);
managers[managers.length - 1] = tm;
SSLContext context = SSLContext.getInstance("TLS");
context.init(new KeyManager[]{ km }, managers, new SecureRandom());
URL url = new URL("https://............/");
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(connection.getSSLSocketFactory());
connection.connect();
If you really don't want to create a new keystore file, then can use KeyStore API to create in memory and load certificate directly.
InputStream is = new FileInputStream("somecert.cer");
// You could get a resource as a stream instead.
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate caCert = (X509Certificate)cf.generateCertificate(is);
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null); // You don't need the KeyStore instance to come from a file.
ks.setCertificateEntry("caCert", caCert);
tmf.init(ks);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
Alternatively, if you want to avoid modifying your default cacerts file, then you'll need to implement your own TrustManager. However a TrustManager needs a keystore to load, so you can either create a new keystore file importing just your certificate.
keytool -import -alias ca -file somecert.cer -keystore truststore.jks -storepass changeit
And use something like following snippet to load the keystore file.
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
// Using null here initialises the TMF with the default trust store.
tmf.init((KeyStore) null);
// Get hold of the default trust manager
X509TrustManager defaultTm = null;
for (TrustManager tm : tmf.getTrustManagers()) {
if (tm instanceof X509TrustManager) {
defaultTm = (X509TrustManager) tm;
break;
}
}
FileInputStream myKeys = new FileInputStream("truststore.jks");
// Do the same with your trust store this time
// Adapt how you load the keystore to your needs
KeyStore myTrustStore = KeyStore.getInstance(KeyStore.getDefaultType());
myTrustStore.load(myKeys, "password".toCharArray());
myKeys.close();
tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(myTrustStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
I'm making a custom HTTP/1.1 server implementation in Java. It's working fine in HTTP mode, but I also want to support HTTPS. I haven't generated a certificate for the server yet, but it should at least be trying to connect. I set the protocol and cipher suite to the same settings as google.com (TLS 1.2, ECDHE_RSA, AES_128_GCM), so I know Chrome supports them.
But when I try to connect to https://localhost in Chrome, it gives ERR_SSL_VERSION_OR_CIPHER_MISMATCH (localhost uses an unsupported protocol) error. On the Java side, I get "no cipher suites in common" error.
Java Code:
public class Server {
private final String dir;
private final ServerSocket server;
private final SSLServerSocket sslServer;
public static String jarDir() {
String uri = ClassLoader.getSystemClassLoader().getResource(".").getPath();
try { return new File(URLDecoder.decode(uri,"UTF-8")).getPath()+File.separator; }
catch (Exception e) { return null; }
}
private static SSLContext createSSLContext(String cert, char[] pass) throws Exception {
/*//Load KeyStore in JKS format:
KeyStore keyStore = KeyStore.getInstance("jks");
keyStore.load(new FileInputStream(cert), pass);
//Create key manager:
KeyManagerFactory kmFactory = KeyManagerFactory.getInstance("SunX509");
kmFactory.init(keyStore, pass); KeyManager[] km = kmFactory.getKeyManagers();
//Create trust manager:
TrustManagerFactory tmFactory = TrustManagerFactory.getInstance("SunX509");
tmFactory.init(keyStore); TrustManager[] tm = tmFactory.getTrustManagers();
//Create SSLContext with protocol:
SSLContext ctx = SSLContext.getInstance("TLSv1.2");
ctx.init(km, tm, null); return ctx;*/
SSLContext ctx = SSLContext.getInstance("TLSv1.2");
ctx.init(null, null, null); return ctx;
}
Server(String localPath, int port) throws Exception {
this(localPath, port, 0);
}
//Server is being initialized with:
//new Server("root", 80, 443);
Server(String localPath, int port, int httpsPort) throws Exception {
dir = localPath; File fdir = new File(jarDir(), dir);
if(!fdir.isDirectory()) throw new Exception("No such directory '"+fdir.getAbsolutePath()+"'!");
//Init Server:
server = new ServerSocket(port);
if(httpsPort > 0) {
SSLContext ctx = createSSLContext("cert.jks", "pass".toCharArray());
sslServer = (SSLServerSocket)ctx.getServerSocketFactory().createServerSocket(httpsPort);
//TLS_DH_anon_WITH_AES_128_GCM_SHA256
sslServer.setEnabledCipherSuites(new String[]{"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"});
sslServer.setEnabledProtocols(new String[]{"TLSv1.2"});
//Also does not work, same error:
//sslServer.setEnabledCipherSuites(sslServer.getSupportedCipherSuites());
//sslServer.setEnabledProtocols(sslServer.getSupportedProtocols());
} else sslServer = null;
/*new Thread(() -> { while(true) try {
new HTTPSocket(server.accept(), this);
} catch(Exception e) { Main.err("HTTP Server Error",e); }}).start();*/
if(httpsPort > 0) new Thread(() -> { while(true) try {
new HTTPSocket(sslServer.accept(), this);
} catch(Exception e) { Main.err("HTTPS Server Error",e); }}).start();
}
/* ... Other Stuff ... */
}
EDIT: I generated a certificate using keytool -genkey -keyalg RSA -alias selfsigned -keystore cert.jks -storepass password -validity 360 -keysize 2048, but now Java throws Keystore was tampered with, or password was incorrect error.
Like I said in the comments, using "password" in keyStore.load solved the issue.
private static SSLContext createSSLContext(String cert, char[] pass) throws Exception {
//Load KeyStore in JKS format:
KeyStore keyStore = KeyStore.getInstance("jks");
keyStore.load(new FileInputStream(cert), "password".toCharArray());
//Create key manager:
KeyManagerFactory kmFactory = KeyManagerFactory.getInstance("SunX509");
kmFactory.init(keyStore, pass); KeyManager[] km = kmFactory.getKeyManagers();
//Create trust manager:
TrustManagerFactory tmFactory = TrustManagerFactory.getInstance("SunX509");
tmFactory.init(keyStore); TrustManager[] tm = tmFactory.getTrustManagers();
//Create SSLContext with protocol:
SSLContext ctx = SSLContext.getInstance("TLSv1.2");
ctx.init(km, tm, null); return ctx;
}
This my code used for usage of key store to save an arbitrary text as a key in the keystore how I am getting the "Keystore is not initialized error", how can I initialise the Keystore?
public void secretKeyGeneration(View view) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
byte[] sek = "eru9tyighw34ilty348934i34uiq34q34ri".getBytes();
SecretKey sk = new SecretKeySpec(sek, 0, sek.length, "AES");
char[] password = "keystorepassword".toCharArray();
KeyStore.ProtectionParameter protParam =
new KeyStore.PasswordProtection(password);
KeyStore.SecretKeyEntry skEntry = new KeyStore.SecretKeyEntry(sk);
ks.setEntry("secretKeyAlias", skEntry, protParam);
}
Keystores have to be initialized and hence you have to call the Keystore.load(...) method. In your case you can for instance invoke:
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null);
byte[] sek = "eru9tyighw34ilty348934i34uiq34q34ri".getBytes();
...
I get external .pem files that need to be converted to .p12 files - I add a username and password in the process. (I need to do this to utilize a third party API.)
Using openssl, the command is...
openssl pkcs12 -export -in xxxx.pem -inkey xxxx.pem -out xxx.p12 -passout pas:newpassword -name "newname"
I can run this from a terminal session and it works perfectly.
However, I will need to do this often and have written a Java class that handles this and more (my application is mostly .jsp with Tomcat and Apache). When I try run the same command from Java using Runtime.exec, I get the dreaded "unable to write 'random state'" error ( Using OpenSSL what does "unable to write 'random state'" mean? ).
I assume that the difference is that, when I run from Java, the user is not "root".
So, is there a better way to convert from pem to .p12 using a Java library rather than executing a command line program (i.e. openssl)?
Otherwise, I guess I need to do some configuration on my server. I can not find any .md file anywhere on the server. The only openssl.cnf file is in a weird directory (/etc/pki/tls). Do I need to create a new openssl.cnf file somewhere else?
This should do what you want to do (using the BouncyCastle PEMReader as suggested above) -- take a PEM-encoded private key + certificate, and output a PKCS#12 file. Uses the same password for the PKCS12 that was used to protect the private key.
public static byte[] pemToPKCS12(final String keyFile, final String cerFile, final String password) throws Exception {
// Get the private key
FileReader reader = new FileReader(keyFile);
PEMReader pem = new PEMReader(reader, new PasswordFinder() {
#Override public char[] getPassword() {
return password.toCharArray();
}
});
PrivateKey key = ((KeyPair)pem.readObject()).getPrivate();
pem.close();
reader.close();
// Get the certificate
reader = new FileReader(cerFile);
pem = new PEMReader(reader);
X509Certificate cert = (X509Certificate)pem.readObject();
pem.close();
reader.close();
// Put them into a PKCS12 keystore and write it to a byte[]
ByteArrayOutputStream bos = new ByteArrayOutputStream();
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(null);
ks.setKeyEntry("alias", (Key)key, password.toCharArray(), new java.security.cert.Certificate[]{cert});
ks.store(bos, password.toCharArray());
bos.close();
return bos.toByteArray();
}
Based on #MugglesMerriweather 's answer, an updated version to v1.51 is the following:
public static byte[] convertPEMToPKCS12(final String keyFile, final String cerFile,
final String password)
throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException
{
// Get the private key
FileReader reader = new FileReader(keyFile);
PEMParser pem = new PEMParser(reader);
PEMKeyPair pemKeyPair = ((PEMKeyPair)pem.readObject());
JcaPEMKeyConverter jcaPEMKeyConverter = new JcaPEMKeyConverter().setProvider("SC");
KeyPair keyPair = jcaPEMKeyConverter.getKeyPair(pemKeyPair);
PrivateKey key = keyPair.getPrivate();
pem.close();
reader.close();
// Get the certificate
reader = new FileReader(cerFile);
pem = new PEMParser(reader);
X509CertificateHolder certHolder = (X509CertificateHolder) pem.readObject();
java.security.cert.Certificate X509Certificate =
new JcaX509CertificateConverter().setProvider("SC")
.getCertificate(certHolder);
pem.close();
reader.close();
// Put them into a PKCS12 keystore and write it to a byte[]
ByteArrayOutputStream bos = new ByteArrayOutputStream();
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(null);
ks.setKeyEntry("alias", (Key) key, password.toCharArray(),
new java.security.cert.Certificate[]{X509Certificate});
ks.store(bos, password.toCharArray());
bos.close();
return bos.toByteArray();
}
In Java, use Bouncycastle but be warned, learning curve is steep and documentation scarce. I strongly recommend you look at the examples which are available as part of the source distribution
Start with the PemReader.
Based on the answers i created a java 7 class, which handles everything for creating a valid SSLContext. Also it creates the necessary chain. TODO: Trustmanager if necessary.
public final class SSL_Context {
private static SSL_Context instance = new SSL_Context();
public static SSL_Context getInstance() {
return instance;
}
private SSLContext sslContext = null;
private SSL_Context() {
try {
sslContext = generateSSLContext();
}
catch (Exception e)
{
ErrorLogger.logException(e);
}
}
final private void dumpKeyStore(KeyStore keyStore)
{
try {
// List the aliases
Enumeration aliases = keyStore.aliases();
for (; aliases.hasMoreElements(); ) {
String alias = (String) aliases.nextElement();
// Does alias refer to a private key?
boolean a = keyStore.isKeyEntry(alias);
// Does alias refer to a trusted certificate?
boolean b = keyStore.isCertificateEntry(alias);
ErrorLogger.log(alias + " " + a + " " + b, 2);
}
} catch (Exception e) {
ErrorLogger.logException(e);
}
}
final private KeyStore convertPEMToPKCS12(final String keyAndPubFile, final String chainFile, final String password) {
try {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
PrivateKey key;
Certificate pubCert;
try (FileReader reader = new FileReader(keyAndPubFile);
PEMParser pem = new PEMParser(reader)) {
PEMKeyPair pemKeyPair = ((PEMKeyPair) pem.readObject());
JcaPEMKeyConverter jcaPEMKeyConverter = new JcaPEMKeyConverter().setProvider("BC");
KeyPair keyPair = jcaPEMKeyConverter.getKeyPair(pemKeyPair);
key = keyPair.getPrivate();
X509CertificateHolder certHolder = (X509CertificateHolder) pem.readObject();
pubCert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certHolder);
}
// Get the certificates
try (FileReader reader = new FileReader(chainFile);
PEMParser pem = new PEMParser(reader)) {
//load all certs
LinkedList<Certificate> certsll = new LinkedList<>();
X509CertificateHolder certHolder = (X509CertificateHolder) pem.readObject();
do {
Certificate X509Certificate = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certHolder);
certsll.add(X509Certificate);
}
while ((certHolder = (X509CertificateHolder) pem.readObject()) != null);
Certificate[] chain = new Certificate[certsll.size()+1];
chain[0] = pubCert;
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(null);
int i = 1;
for (Certificate cert : certsll) {
ks.setCertificateEntry("chain" + i, cert);
chain[i] = ks.getCertificate("chain" + i);
i++;
}
ks.setKeyEntry("cert", key, password.toCharArray(), chain);
return ks;
}
}
catch (Exception e)
{
ErrorLogger.logException(e);
}
return null;
}
final private SSLContext generateSSLContext()
{
String keyStorePassword = "";
try {
KeyStore keyStore = convertPEMToPKCS12("ssl/keyandcert.pem", "ssl/ca_bundle.crt", keyStorePassword);
SSLContext sslContext = SSLContext.getInstance("TLSv1");
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, keyStorePassword.toCharArray());
sslContext.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom());
return sslContext;
} catch (Exception e) {
ErrorLogger.logException(e);
}
return null;
}
final public SSLContext getContext() {
return sslContext;
}
final public static void main(String args[])
{
getInstance().getContext();
}
}
This solutions is an adaptation of #sascha-arthur's to accomodate for:
Handles edge-case where PrivateKey format is not as expected.
Gracefully handle scenario where public key is not available
Fixed a few minor redundancies and formatting
The code:
String alias="myalias";
char[] password = "mypassword".toCharArray();
// Private Key
PEMParser pem = new PEMParser(new FileReader(keyFile));
Object parsedObject = pem.readObject();
PrivateKeyInfo privateKeyInfo = parsedObject instanceof PEMKeyPair ? ((PEMKeyPair)parsedObject).getPrivateKeyInfo() : (PrivateKeyInfo)parsedObject;
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyInfo.getEncoded());
KeyFactory factory = KeyFactory.getInstance("RSA");
PrivateKey key = factory.generatePrivate(privateKeySpec);
List<X509Certificate> certs = new ArrayList<>();
X509CertificateHolder certHolder = (X509CertificateHolder)pem.readObject();
if(certHolder != null) {
certs.add(new JcaX509CertificateConverter().setProvider("BC").getCertificate(certHolder));
}
// Certificate
pem = new PEMParser(new FileReader(certFile));
while((certHolder = (X509CertificateHolder)pem.readObject()) != null) {
certs.add(new JcaX509CertificateConverter().setProvider("BC").getCertificate(certHolder));
}
// Keystore
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(null);
for (int i = 0; i < certs.size(); i++) {
ks.setCertificateEntry(alias + "_" + i, certs.get(i));
}
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(null);
keyStore.setKeyEntry(alias, key, password, certs.toArray(new X509Certificate[certs.size()]));
For this to work with a LetsEncrypt certificate, you'll need to use the following files:
privkey.pem
fullchain.pem