/******************************************************************************* * (C) Copyright 2014 Teknux.org (http://teknux.org/). * * 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. * * Contributors: * "Pierre PINON" * "Francois EYL" * "Laurent MARCHAL" * *******************************************************************************/ package org.teknux.jettybootstrap.keystore; import java.io.IOException; import java.io.InputStream; import java.security.KeyFactory; import java.security.KeyStore; import java.security.KeyStore.Entry; import java.security.KeyStore.PrivateKeyEntry; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.UnrecoverableEntryException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.util.Enumeration; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.io.IOUtils; import org.bouncycastle.util.encoders.Base64; import org.bouncycastle.util.encoders.DecoderException; public class JettyKeystoreConvertorBuilder extends AbstractJettyKeystore { private static final String CERTIFICATE_TYPE_X509 = "X.509"; private static final String PRIVATE_KEY_REGEX = "-----BEGIN [^-]*PRIVATE KEY-----\n([^-]*)\n-----END [^-]*PRIVATE KEY-----"; private static final Pattern PRIVATE_KEY_PATTERN = Pattern.compile(PRIVATE_KEY_REGEX, Pattern.DOTALL); private static final String KEYSTORE_TYPE_JKS = "JKS"; private static final String KEYSTORE_TYPE_PKCS12 = "PKCS12"; private static final String DEFAULT_ALGORITHM = ALGORITHM_RSA; private static final String DEFAULT_CERTIFICATE_TYPE = CERTIFICATE_TYPE_X509; private PrivateKey privateKey; private Certificate certificate; public KeyStore build(String alias, String password) throws JettyKeystoreException { return build(alias, password, true, false); } public KeyStore build(String alias, String password, boolean checkValidity, boolean verifySignature) throws JettyKeystoreException { Objects.requireNonNull(alias, "Alias is required"); Objects.requireNonNull(password, "Password is required"); if (privateKey != null && certificate != null) { KeyStore keystore = createKeyStore(privateKey, certificate, alias, password); if (checkValidity | verifySignature) { checkValidity(keystore, alias, checkValidity, verifySignature); } return keystore; } else { throw new JettyKeystoreException(JettyKeystoreException.ERROR_CREATE_KEYSTORE, "Can not create keystore file"); } } public JettyKeystoreConvertorBuilder setKeystore(InputStream inputStream, String password) throws JettyKeystoreException { return setKeystore(inputStream, password, null); } public JettyKeystoreConvertorBuilder setKeystore(InputStream inputStream, String password, String alias) throws JettyKeystoreException { return setKeystore(inputStream, password, alias, KEYSTORE_TYPE_JKS); } public JettyKeystoreConvertorBuilder setKeystore(InputStream inputStream, String password, String alias, String type) throws JettyKeystoreException { KeyStore keystore = loadKeyStore(inputStream, password, type); try { PrivateKeyEntry privateKeyEntry = getPrivateKeyEntryOfKeyStore(keystore, password, alias); privateKey = privateKeyEntry.getPrivateKey(); certificate = privateKeyEntry.getCertificate(); } catch (JettyKeystoreException e) { throw new JettyKeystoreException(JettyKeystoreException.ERROR_LOAD_KEYSTORE, "Can not load file (Keystore)", e); } return this; } public JettyKeystoreConvertorBuilder setPrivateKeyFromKeystore(InputStream inputStream, String password) throws JettyKeystoreException { return setPrivateKeyFromKeystore(inputStream, password, null); } public JettyKeystoreConvertorBuilder setPrivateKeyFromKeystore(InputStream inputStream, String password, String alias) throws JettyKeystoreException { return setPrivateKeyFromKeystore(inputStream, password, alias, KEYSTORE_TYPE_JKS); } public JettyKeystoreConvertorBuilder setPrivateKeyFromKeystore(InputStream inputStream, String password, String alias, String type) throws JettyKeystoreException { KeyStore keystore = loadKeyStore(inputStream, password, type); try { PrivateKeyEntry privateKeyEntry = getPrivateKeyEntryOfKeyStore(keystore, password, alias); privateKey = privateKeyEntry.getPrivateKey(); } catch (JettyKeystoreException e) { throw new JettyKeystoreException(JettyKeystoreException.ERROR_LOAD_KEYSTORE, "Can not load file (Keystore)", e); } return this; } public JettyKeystoreConvertorBuilder setCertificateFromKeystore(InputStream inputStream, String password) throws JettyKeystoreException { return setCertificateFromKeystore(inputStream, password, null); } public JettyKeystoreConvertorBuilder setCertificateFromKeystore(InputStream inputStream, String password, String alias) throws JettyKeystoreException { return setCertificateFromKeystore(inputStream, password, alias, KEYSTORE_TYPE_JKS); } public JettyKeystoreConvertorBuilder setCertificateFromKeystore(InputStream inputStream, String password, String alias, String type) throws JettyKeystoreException { KeyStore keystore = loadKeyStore(inputStream, password, type); try { PrivateKeyEntry privateKeyEntry = getPrivateKeyEntryOfKeyStore(keystore, password, alias); certificate = privateKeyEntry.getCertificate(); } catch (JettyKeystoreException e) { throw new JettyKeystoreException(JettyKeystoreException.ERROR_LOAD_KEYSTORE, "Can not load file (Keystore)", e); } return this; } public JettyKeystoreConvertorBuilder setPKCS8(InputStream inputStream) throws JettyKeystoreException { try { return setCertificateFromPKCS8(inputStream).setPrivateKeyFromPKCS8(inputStream); } catch (JettyKeystoreException e) { throw new JettyKeystoreException(JettyKeystoreException.ERROR_LOAD_PKCS8, "Can not load file (PKCS8)", e); } } public JettyKeystoreConvertorBuilder setPKCS8(InputStream inputStream, String privateKeyAlgorythm, String certificateType) throws JettyKeystoreException { try { return setPrivateKeyFromPKCS8(inputStream, privateKeyAlgorythm).setCertificateFromPKCS8(inputStream, certificateType); } catch (JettyKeystoreException e) { throw new JettyKeystoreException(JettyKeystoreException.ERROR_LOAD_PKCS8, "Can not load file (PKCS8)", e); } } public JettyKeystoreConvertorBuilder setPrivateKeyFromPKCS8(InputStream inputStream) throws JettyKeystoreException { return setPrivateKeyFromPKCS8(inputStream, DEFAULT_ALGORITHM); } public JettyKeystoreConvertorBuilder setPrivateKeyFromPKCS8(InputStream inputStream, String algorithm) throws JettyKeystoreException { try { String contentKeyFile = IOUtils.toString(inputStream); Matcher contentKeyMatcher = PRIVATE_KEY_PATTERN.matcher(contentKeyFile); String contentKey; if (contentKeyMatcher.find()) { contentKey = contentKeyMatcher.group(1); } else { contentKey = contentKeyFile; } byte[] decodedKeyFile = Base64.decode(contentKey); KeyFactory keyFactory = KeyFactory.getInstance(algorithm); PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(decodedKeyFile); privateKey = keyFactory.generatePrivate(privateKeySpec); return this; } catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException | DecoderException e) { throw new JettyKeystoreException(JettyKeystoreException.ERROR_LOAD_PRIVATE_KEY_PKCS8, "Can not load private key (PKCS8)", e); } } public JettyKeystoreConvertorBuilder setCertificateFromPKCS8(InputStream inputStream) throws JettyKeystoreException { return setCertificateFromPKCS8(inputStream, DEFAULT_CERTIFICATE_TYPE); } public JettyKeystoreConvertorBuilder setCertificateFromPKCS8(InputStream inputStream, String certificateType) throws JettyKeystoreException { try { CertificateFactory certificateFactory = CertificateFactory.getInstance(certificateType); certificate = certificateFactory.generateCertificate(inputStream); return this; } catch (CertificateException e) { throw new JettyKeystoreException(JettyKeystoreException.ERROR_LOAD_CERTIFICATE_PKCS8, "Can not load certificate (PKCS8)", e); } } public JettyKeystoreConvertorBuilder setPKCS12(InputStream inputStream, String password) throws JettyKeystoreException { return setPKCS12(inputStream, password, null); } public JettyKeystoreConvertorBuilder setPKCS12(InputStream inputStream, String password, String alias) throws JettyKeystoreException { try { setKeystore(inputStream, password, alias, KEYSTORE_TYPE_PKCS12); } catch (JettyKeystoreException e) { throw new JettyKeystoreException(JettyKeystoreException.ERROR_LOAD_PKCS12, "Can not load file (PKCS12)", e); } return this; } public JettyKeystoreConvertorBuilder setPrivateKeyFromPKCS12(InputStream inputStream, String password) throws JettyKeystoreException { return setPrivateKeyFromPKCS12(inputStream, password, null); } public JettyKeystoreConvertorBuilder setPrivateKeyFromPKCS12(InputStream inputStream, String password, String alias) throws JettyKeystoreException { try { return setPrivateKeyFromKeystore(inputStream, password, alias, KEYSTORE_TYPE_PKCS12); } catch (JettyKeystoreException e) { throw new JettyKeystoreException(JettyKeystoreException.ERROR_LOAD_PRIVATE_KEY_PKCS12, "Can not load private key (PKCS12)", e); } } public JettyKeystoreConvertorBuilder setCertificateFromPKCS12(InputStream inputStream, String password) throws JettyKeystoreException { return setCertificateFromPKCS12(inputStream, password, null); } public JettyKeystoreConvertorBuilder setCertificateFromPKCS12(InputStream inputStream, String password, String alias) throws JettyKeystoreException { try { return setCertificateFromKeystore(inputStream, password, alias, KEYSTORE_TYPE_PKCS12); } catch (JettyKeystoreException e) { throw new JettyKeystoreException(JettyKeystoreException.ERROR_LOAD_CERTIFICATE_PKCS12, "Can not load certificate (PKCS12)", e); } } private static KeyStore loadKeyStore(InputStream inputStream, String password, String type) throws JettyKeystoreException { try { if (password == null) { throw new JettyKeystoreException(JettyKeystoreException.ERROR_LOAD_KEYSTORE, "KeyStore Password can not be null (" + type + ")"); } KeyStore keystore = KeyStore.getInstance(type); keystore.load(inputStream, password.toCharArray()); return keystore; } catch (CertificateException | KeyStoreException | NoSuchAlgorithmException | IOException e) { throw new JettyKeystoreException(JettyKeystoreException.ERROR_LOAD_KEYSTORE, "Can not load KeyStore (" + type + ")", e); } } private static PrivateKeyEntry getPrivateKeyEntryOfKeyStore(KeyStore keystore, String password, String alias) throws JettyKeystoreException { try { if (alias == null) { Enumeration<String> aliasEnumeration = keystore.aliases(); while (aliasEnumeration.hasMoreElements()) { String aliasItem = (String) aliasEnumeration.nextElement(); if (keystore.isKeyEntry(aliasItem)) { Entry entry = keystore.getEntry(aliasItem, new KeyStore.PasswordProtection(password.toCharArray())); if (entry instanceof PrivateKeyEntry) { return (PrivateKeyEntry) entry; } } } } else { Entry entry = keystore.getEntry(alias, new KeyStore.PasswordProtection(password.toCharArray())); if (entry instanceof PrivateKeyEntry) { return (PrivateKeyEntry) entry; } } throw new JettyKeystoreException(JettyKeystoreException.ERROR_UNREACHABLE_PRIVATE_KEY_ENTRY, "Can not find private key entry"); } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableEntryException e) { throw new JettyKeystoreException(JettyKeystoreException.ERROR_UNREACHABLE_PRIVATE_KEY_ENTRY, "Can not find private key entry", e); } } public void checkValidity(boolean checkValidity, boolean verifySignature) throws JettyKeystoreException { build("testAlias", "testPassword", checkValidity, verifySignature); } }