/** * Copyright (c) Codice Foundation * <p> * This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software Foundation, either version 3 of the * License, or any later version. * <p> * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. A copy of the GNU Lesser General Public License * is distributed along with this program and can be found at * <http://www.gnu.org/licenses/lgpl.html>. **/ package org.codice.ddf.security.certificate.keystore.editor; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.Reader; import java.lang.management.ManagementFactory; import java.net.InetSocketAddress; import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.GeneralSecurityException; import java.security.Key; import java.security.KeyManagementException; import java.security.KeyPair; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.Security; import java.security.UnrecoverableKeyException; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Base64; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.management.InstanceAlreadyExistsException; import javax.management.MBeanServer; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.ssl.PKCS8Key; import org.apache.http.HttpHost; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Set; import org.bouncycastle.asn1.cms.CMSObjectIdentifiers; import org.bouncycastle.asn1.cms.ContentInfo; import org.bouncycastle.asn1.cms.OriginatorInfo; import org.bouncycastle.asn1.cms.SignedData; import org.bouncycastle.asn1.x500.RDN; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x500.style.BCStyle; import org.bouncycastle.asn1.x500.style.IETFUtils; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; import org.bouncycastle.cms.CMSEnvelopedData; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.openssl.PEMEncryptedKeyPair; import org.bouncycastle.openssl.PEMKeyPair; import org.bouncycastle.openssl.PEMParser; import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder; import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ddf.security.SecurityConstants; import ddf.security.common.audit.SecurityLogger; public class KeystoreEditor implements KeystoreEditorMBean { private static final Logger LOGGER = LoggerFactory.getLogger(KeystoreEditor.class); protected static final String CERT_TYPE = "application/x-x509-ca-cert"; protected static final String PEM_TYPE = "application/x-pem-file"; protected static final String DER_TYPE = "application/x-x509-ca-cert"; protected static final String PKCS7_TYPE = "application/x-pkcs7-certificates"; protected static final String PKCS12_TYPE = "application/x-pkcs12"; protected static final String JKS_TYPE = "application/x-java-keystore"; private KeyStore keyStore; private KeyStore trustStore; public KeystoreEditor() { registerMbean(); addProvider(); init(); } private void addProvider() { Security.addProvider(new BouncyCastleProvider()); } private void init() { try { keyStore = SecurityConstants.newKeystore(); trustStore = SecurityConstants.newTruststore(); } catch (KeyStoreException e) { LOGGER.info("Unable to create keystore instance of type {}", System.getProperty(SecurityConstants.KEYSTORE_TYPE), e); } Path keyStoreFile = Paths.get(SecurityConstants.getKeystorePath()); Path trustStoreFile = Paths.get(SecurityConstants.getTruststorePath()); Path ddfHomePath = Paths.get(System.getProperty("ddf.home")); if (!keyStoreFile.isAbsolute()) { keyStoreFile = Paths.get(ddfHomePath.toString(), keyStoreFile.toString()); } if (!trustStoreFile.isAbsolute()) { trustStoreFile = Paths.get(ddfHomePath.toString(), trustStoreFile.toString()); } String keyStorePassword = SecurityConstants.getKeystorePassword(); String trustStorePassword = SecurityConstants.getTruststorePassword(); if (!Files.isReadable(keyStoreFile) || !Files.isReadable(trustStoreFile)) { LOGGER.info("Unable to read system key/trust store files: [ {} ] [ {} ]", keyStoreFile, trustStoreFile); return; } try (InputStream kfis = Files.newInputStream(keyStoreFile)) { keyStore.load(kfis, keyStorePassword.toCharArray()); } catch (NoSuchAlgorithmException | CertificateException | IOException e) { LOGGER.info("Unable to load system key file.", e); try { keyStore.load(null, null); } catch (NoSuchAlgorithmException | CertificateException | IOException ignore) { } } try (InputStream tfis = Files.newInputStream(trustStoreFile)) { trustStore.load(tfis, trustStorePassword.toCharArray()); } catch (NoSuchAlgorithmException | CertificateException | IOException e) { LOGGER.info("Unable to load system trust file.", e); try { trustStore.load(null, null); } catch (NoSuchAlgorithmException | CertificateException | IOException ignore) { } } } private void registerMbean() { ObjectName objectName = null; MBeanServer mBeanServer = null; try { objectName = new ObjectName(KeystoreEditor.class.getName() + ":service=keystore"); mBeanServer = ManagementFactory.getPlatformMBeanServer(); } catch (MalformedObjectNameException e) { LOGGER.info("Unable to create Keystore Editor MBean.", e); } if (mBeanServer != null) { try { try { mBeanServer.registerMBean(this, objectName); LOGGER.debug("Registered Keystore Editor MBean under object name: {}", objectName.toString()); } catch (InstanceAlreadyExistsException e) { // Try to remove and re-register mBeanServer.unregisterMBean(objectName); mBeanServer.registerMBean(this, objectName); LOGGER.debug("Re-registered Keystore Editor MBean"); } } catch (Exception e) { //objectName is not always non-null because new ObjectName(...) can throw an exception LOGGER.info("Could not register MBean [{}].", objectName != null ? objectName.toString() : KeystoreEditor.class.getName(), e); } } } @Override public List<Map<String, Object>> getKeystore() { return getKeyStoreInfo(keyStore); } @Override public List<Map<String, Object>> getTruststore() { return getKeyStoreInfo(trustStore); } private List<Map<String, Object>> getKeyStoreInfo(KeyStore store) { List<Map<String, Object>> storeEntries = new ArrayList<>(); try { Enumeration<String> aliases = store.aliases(); while (aliases.hasMoreElements()) { String alias = aliases.nextElement(); Map<String, Object> aliasMap = new HashMap<>(); Certificate certificate = store.getCertificate(alias); boolean isKey = store.isKeyEntry(alias); aliasMap.put("alias", alias); aliasMap.put("isKey", isKey); aliasMap.put("type", certificate.getType()); aliasMap.put("format", certificate.getPublicKey() .getFormat()); aliasMap.put("algorithm", certificate.getPublicKey() .getAlgorithm()); storeEntries.add(aliasMap); } } catch (KeyStoreException e) { LOGGER.info("Unable to read entries from keystore.", e); } return storeEntries; } @Override public void addPrivateKey(String alias, String keyPassword, String storePassword, String data, String type, String fileName) throws KeystoreEditorException { SecurityLogger.audit("Adding alias {} to private key", alias); LOGGER.trace("Received data {}", data); Path keyStoreFile = Paths.get(SecurityConstants.getKeystorePath()); if (!keyStoreFile.isAbsolute()) { Path ddfHomePath = Paths.get(System.getProperty("ddf.home")); keyStoreFile = Paths.get(ddfHomePath.toString(), keyStoreFile.toString()); } String keyStorePassword = SecurityConstants.getKeystorePassword(); addToStore(alias, keyPassword, storePassword, data, type, fileName, keyStoreFile.toString(), keyStorePassword, keyStore); } @Override public void addTrustedCertificate(String alias, String keyPassword, String storePassword, String data, String type, String fileName) throws KeystoreEditorException { SecurityLogger.audit("Adding alias {} to trust store", alias); LOGGER.trace("Received data {}", data); Path trustStoreFile = Paths.get(SecurityConstants.getTruststorePath()); if (!trustStoreFile.isAbsolute()) { Path ddfHomePath = Paths.get(System.getProperty("ddf.home")); trustStoreFile = Paths.get(ddfHomePath.toString(), trustStoreFile.toString()); } String trustStorePassword = SecurityConstants.getTruststorePassword(); addToStore(alias, keyPassword, storePassword, data, type, fileName, trustStoreFile.toString(), trustStorePassword, trustStore); } @Override public List<Map<String, Object>> addTrustedCertificateFromUrl(String url) { SSLSocket socket = null; String decodedUrl = null; List<Map<String, Object>> resultList = new ArrayList<>(); try { decodedUrl = new String(Base64.getDecoder() .decode(url), "UTF-8"); socket = createNonVerifyingSslSocket(decodedUrl); socket.startHandshake(); X509Certificate[] peerCertificateChain = (X509Certificate[]) socket.getSession() .getPeerCertificates(); for (X509Certificate certificate : peerCertificateChain) { try { X500Name x500name = new JcaX509CertificateHolder(certificate).getSubject(); RDN cn = x500name.getRDNs(BCStyle.CN)[0]; String cnStr = IETFUtils.valueToString(cn.getFirst() .getValue()); trustStore.setCertificateEntry(cnStr, certificate); resultList.add(Collections.singletonMap("success", true)); } catch (CertificateEncodingException e) { resultList.add(Collections.singletonMap("success", false)); LOGGER.info("Unable to store certificate: {}", certificate.toString(), e); } } Path trustStoreFile = Paths.get(SecurityConstants.getTruststorePath()); if (!trustStoreFile.isAbsolute()) { Path ddfHomePath = Paths.get(System.getProperty("ddf.home")); trustStoreFile = Paths.get(ddfHomePath.toString(), trustStoreFile.toString()); } String keyStorePassword = SecurityConstants.getTruststorePassword(); OutputStream fos = Files.newOutputStream(trustStoreFile); trustStore.store(fos, keyStorePassword.toCharArray()); } catch (IOException | GeneralSecurityException e) { LOGGER.info("Unable to add certificate(s) to trust store from URL: {}", (decodedUrl != null) ? decodedUrl : url, e); } finally { IOUtils.closeQuietly(socket); } return resultList; } @Override public List<Map<String, Object>> certificateDetails(String url) { List<Map<String, Object>> certificates = new ArrayList<>(); SSLSocket socket = null; String decodedUrl = null; try { decodedUrl = new String(Base64.getDecoder() .decode(url), "UTF-8"); socket = createNonVerifyingSslSocket(decodedUrl); socket.startHandshake(); X509Certificate[] peerCertificateChain = (X509Certificate[]) socket.getSession() .getPeerCertificates(); for (X509Certificate certificate : peerCertificateChain) { Map<String, Object> certMap = new HashMap<>(); certMap.put("type", certificate.getType()); certMap.put("issuerDn", certificate.getIssuerDN()); certMap.put("subjectDn", certificate.getSubjectDN()); certMap.put("extendedKeyUsage", certificate.getExtendedKeyUsage()); certMap.put("notAfter", certificate.getNotAfter() .toString()); certMap.put("notBefore", certificate.getNotBefore() .toString()); certMap.put("thumbprint", DigestUtils.sha1Hex(certificate.getEncoded())); certificates.add(certMap); } } catch (IOException | GeneralSecurityException e) { LOGGER.info("Unable to parse certificate from URL: {}", (decodedUrl != null) ? decodedUrl : url, e); } finally { IOUtils.closeQuietly(socket); } return certificates; } SSLSocket createNonVerifyingSslSocket(String decodedUrl) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException, IOException { URL httpsUrl = new URL(decodedUrl); SSLContext sslContext = SSLContext.getInstance("TLS"); TrustManager tm = new NonVerifyingTrustManager(); sslContext.init(null, new TrustManager[] {tm}, null); SSLConnectionSocketFactory sslSocketFactory = new NonVerifyingSslSocketFactory(sslContext); return (SSLSocket) sslSocketFactory.connectSocket(30000, null, new HttpHost(httpsUrl.getHost()), new InetSocketAddress(httpsUrl.getHost(), httpsUrl.getPort()), null, null); } @Override public List<String> replaceSystemStores(String fqdn, String keyPassword, String keystorePassword, String keystoreData, String keystoreFileName, String truststorePassword, String truststoreData, String truststoreFileName) throws KeystoreEditorException { List<String> errors = new ArrayList<>(); if (StringUtils.isEmpty(keystoreFileName) || StringUtils.isEmpty(keystoreData) || StringUtils.isEmpty(keystorePassword) || StringUtils.isEmpty(keyPassword)) { errors.add("Some of the required keystore fields are missing"); } if (StringUtils.isEmpty(truststoreFileName) || StringUtils.isEmpty(truststoreData) || StringUtils.isEmpty(truststorePassword)) { errors.add("Some of the required truststore fields are missing"); } if (errors.size() > 0) { return errors; } try { if (StringUtils.isEmpty(fqdn) || !validKeystoreAlias(fqdn, keystorePassword, keystoreData, keystoreFileName)) { errors.add("Keystore does not contain the required key for " + fqdn); return errors; } deleteAllSystemStoreEntries(); addPrivateKey(fqdn, keyPassword, keystorePassword, keystoreData, null, keystoreFileName); addTrustedCertificate(fqdn, null, truststorePassword, truststoreData, null, truststoreFileName); } catch (Exception e) { LOGGER.info("Unable to replace system stores.", e); errors.add( "Unable to replace system stores. Most likely due to invalid password or invalid store type"); } return errors; } private boolean validKeystoreAlias(String alias, String keystorePassword, String keystoreData, String keystoreFileName) throws KeystoreEditorException { boolean valid = false; try (InputStream inputStream = new ByteArrayInputStream(Base64.getDecoder() .decode(keystoreData))) { if (StringUtils.isBlank(alias)) { throw new IllegalArgumentException("Alias cannot be null."); } KeyStore ks = null; if (StringUtils.endsWithIgnoreCase(keystoreFileName, ".p12")) { ks = KeyStore.getInstance("PKCS12"); } else if (StringUtils.endsWithIgnoreCase(keystoreFileName, ".jks")) { ks = KeyStore.getInstance("jks"); } if (ks != null) { ks.load(inputStream, keystorePassword.toCharArray()); valid = ks.containsAlias(alias); } } catch (Exception e) { LOGGER.info("Unable read keystore data.", e); throw new KeystoreEditorException("Unable read keystore data.", e); } return valid; } private synchronized void addToStore(String alias, String keyPassword, String storePassword, String data, String type, String fileName, String path, String storepass, KeyStore store) throws KeystoreEditorException { OutputStream fos = null; try (InputStream inputStream = new ByteArrayInputStream(Base64.getDecoder() .decode(data))) { if (StringUtils.isBlank(alias)) { throw new IllegalArgumentException("Alias cannot be null."); } Path storeFile = Paths.get(path); //check the two most common key/cert stores first (pkcs12 and jks) if (PKCS12_TYPE.equals(type) || StringUtils.endsWithIgnoreCase(fileName, ".p12")) { //priv key + cert chain KeyStore pkcs12Store = KeyStore.getInstance("PKCS12"); pkcs12Store.load(inputStream, storePassword.toCharArray()); Certificate[] chain = pkcs12Store.getCertificateChain(alias); Key key = pkcs12Store.getKey(alias, keyPassword.toCharArray()); if (key != null) { store.setKeyEntry(alias, key, keyPassword.toCharArray(), chain); fos = Files.newOutputStream(storeFile); store.store(fos, storepass.toCharArray()); } } else if (JKS_TYPE.equals(type) || StringUtils.endsWithIgnoreCase(fileName, ".jks")) { //java keystore file KeyStore jks = KeyStore.getInstance("jks"); jks.load(inputStream, storePassword.toCharArray()); Enumeration<String> aliases = jks.aliases(); //we are going to store all entries from the jks regardless of the passed in alias while (aliases.hasMoreElements()) { String jksAlias = aliases.nextElement(); if (jks.isKeyEntry(jksAlias)) { Key key = jks.getKey(jksAlias, keyPassword.toCharArray()); Certificate[] certificateChain = jks.getCertificateChain(jksAlias); store.setKeyEntry(jksAlias, key, keyPassword.toCharArray(), certificateChain); } else { Certificate certificate = jks.getCertificate(jksAlias); store.setCertificateEntry(jksAlias, certificate); } } fos = Files.newOutputStream(storeFile); store.store(fos, storepass.toCharArray()); //need to parse der separately from pem, der has the same mime type but is binary hence checking both } else if (DER_TYPE.equals(type) && StringUtils.endsWithIgnoreCase(fileName, ".der")) { ASN1InputStream asn1InputStream = new ASN1InputStream(inputStream); ASN1Primitive asn1Primitive = asn1InputStream.readObject(); X509CertificateHolder x509CertificateHolder = new X509CertificateHolder( asn1Primitive.getEncoded()); CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509", "BC"); Certificate certificate = certificateFactory.generateCertificate(new ByteArrayInputStream( x509CertificateHolder.getEncoded())); X500Name x500name = new JcaX509CertificateHolder((X509Certificate) certificate).getSubject(); RDN cn = x500name.getRDNs(BCStyle.CN)[0]; String cnStr = IETFUtils.valueToString(cn.getFirst() .getValue()); if (!store.isCertificateEntry(cnStr) && !store.isKeyEntry(cnStr)) { store.setCertificateEntry(cnStr, certificate); } store.setCertificateEntry(alias, certificate); fos = Files.newOutputStream(storeFile); store.store(fos, storepass.toCharArray()); //if it isn't one of the stores we support, it might be a key or cert by itself } else if (isPemParsable(type, fileName)) { //This is the catch all case for PEM, P7B, etc. with common file extensions if the mime type isn't read correctly in the browser Reader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); PEMParser pemParser = new PEMParser(reader); Object object; boolean setEntry = false; while ((object = pemParser.readObject()) != null) { if (object instanceof PEMEncryptedKeyPair || object instanceof PEMKeyPair) { PEMKeyPair pemKeyPair; if (object instanceof PEMEncryptedKeyPair) { PEMEncryptedKeyPair pemEncryptedKeyPairKeyPair = (PEMEncryptedKeyPair) object; JcePEMDecryptorProviderBuilder jcePEMDecryptorProviderBuilder = new JcePEMDecryptorProviderBuilder(); pemKeyPair = pemEncryptedKeyPairKeyPair.decryptKeyPair( jcePEMDecryptorProviderBuilder.build(keyPassword.toCharArray())); } else { pemKeyPair = (PEMKeyPair) object; } KeyPair keyPair = new JcaPEMKeyConverter().setProvider("BC") .getKeyPair(pemKeyPair); PrivateKey privateKey = keyPair.getPrivate(); Certificate[] chain = store.getCertificateChain(alias); if (chain == null) { chain = buildCertChain(alias, store); } store.setKeyEntry(alias, privateKey, keyPassword.toCharArray(), chain); setEntry = true; } else if (object instanceof X509CertificateHolder) { X509CertificateHolder x509CertificateHolder = (X509CertificateHolder) object; CertificateFactory certificateFactory = CertificateFactory.getInstance( "X.509", "BC"); Certificate certificate = certificateFactory.generateCertificate(new ByteArrayInputStream( x509CertificateHolder.getEncoded())); X500Name x500name = new JcaX509CertificateHolder((X509Certificate) certificate).getSubject(); RDN cn = x500name.getRDNs(BCStyle.CN)[0]; String cnStr = IETFUtils.valueToString(cn.getFirst() .getValue()); if (!store.isCertificateEntry(cnStr) && !store.isKeyEntry(cnStr)) { store.setCertificateEntry(cnStr, certificate); } store.setCertificateEntry(alias, certificate); setEntry = true; } else if (object instanceof ContentInfo) { ContentInfo contentInfo = (ContentInfo) object; if (contentInfo.getContentType() .equals(CMSObjectIdentifiers.envelopedData)) { CMSEnvelopedData cmsEnvelopedData = new CMSEnvelopedData(contentInfo); OriginatorInfo originatorInfo = cmsEnvelopedData.getOriginatorInfo() .toASN1Structure(); ASN1Set certificates = originatorInfo.getCertificates(); setEntry = importASN1CertificatesToStore(store, setEntry, certificates); } else if (contentInfo.getContentType() .equals(CMSObjectIdentifiers.signedData)) { SignedData signedData = SignedData.getInstance(contentInfo.getContent()); ASN1Set certificates = signedData.getCertificates(); setEntry = importASN1CertificatesToStore(store, setEntry, certificates); } } else if (object instanceof PKCS8EncryptedPrivateKeyInfo) { PKCS8EncryptedPrivateKeyInfo pkcs8EncryptedPrivateKeyInfo = (PKCS8EncryptedPrivateKeyInfo) object; Certificate[] chain = store.getCertificateChain(alias); if (chain == null) { chain = buildCertChain(alias, store); } try { store.setKeyEntry(alias, pkcs8EncryptedPrivateKeyInfo.getEncoded(), chain); setEntry = true; } catch (KeyStoreException keyEx) { try { PKCS8Key pkcs8Key = new PKCS8Key(pkcs8EncryptedPrivateKeyInfo.getEncoded(), keyPassword.toCharArray()); store.setKeyEntry(alias, pkcs8Key.getPrivateKey(), keyPassword.toCharArray(), chain); setEntry = true; } catch (GeneralSecurityException e) { LOGGER.info( "Unable to add PKCS8 key to keystore with secondary method. Throwing original exception.", e); throw keyEx; } } } } if (setEntry) { fos = Files.newOutputStream(storeFile); store.store(fos, storepass.toCharArray()); } } } catch (Exception e) { LOGGER.info("Unable to add entry {} to store", alias, e); throw new KeystoreEditorException("Unable to add entry " + alias + " to store", e); } finally { if (fos != null) { try { fos.close(); } catch (IOException ignore) { } } } init(); } private boolean isPemParsable(String type, String fileName) { //check mime types if (PKCS7_TYPE.equals(type) || CERT_TYPE.equals(type) || PEM_TYPE.equals(type)) { return true; //check file extensions } else if (StringUtils.endsWithIgnoreCase(fileName, ".crt") || StringUtils.endsWithIgnoreCase(fileName, ".key") || StringUtils.endsWithIgnoreCase(fileName, ".pem") || StringUtils.endsWithIgnoreCase(fileName, ".p7b")) { return true; } return false; } private boolean importASN1CertificatesToStore(KeyStore store, boolean setEntry, ASN1Set certificates) throws KeystoreEditorException { Enumeration certificateEnumeration = certificates.getObjects(); try { while (certificateEnumeration.hasMoreElements()) { ASN1Primitive asn1Primitive = ((ASN1Encodable) certificateEnumeration.nextElement()).toASN1Primitive(); org.bouncycastle.asn1.x509.Certificate instance = org.bouncycastle.asn1.x509.Certificate.getInstance(asn1Primitive); CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509", "BC"); Certificate certificate = certificateFactory.generateCertificate(new ByteArrayInputStream(instance.getEncoded())); X500Name x500name = new JcaX509CertificateHolder((X509Certificate) certificate).getSubject(); RDN cn = x500name.getRDNs(BCStyle.CN)[0]; store.setCertificateEntry(IETFUtils.valueToString(cn.getFirst() .getValue()), certificate); setEntry = true; } } catch (CertificateException | NoSuchProviderException | KeyStoreException | IOException e) { throw new KeystoreEditorException("Unable to import ASN1 certificates to store", e); } return setEntry; } private Certificate[] buildCertChain(String alias, KeyStore store) throws KeystoreEditorException { List<Certificate> certificates = buildCertChainList(alias, store); Certificate[] certificateArr = new Certificate[certificates.size()]; for (int i = 0; i < certificates.size(); i++) { certificateArr[i] = certificates.get(i); } return certificateArr; } private List<Certificate> buildCertChainList(String alias, KeyStore store) throws KeystoreEditorException { try { Certificate certificate = store.getCertificate(alias); if (certificate != null) { X500Name x500nameSubject = new JcaX509CertificateHolder((X509Certificate) certificate).getSubject(); RDN subjectCn = x500nameSubject.getRDNs(BCStyle.CN)[0]; X500Name x500nameIssuer = new JcaX509CertificateHolder((X509Certificate) certificate).getIssuer(); RDN issuerCn = x500nameIssuer.getRDNs(BCStyle.CN)[0]; String issuer = IETFUtils.valueToString(issuerCn.getFirst() .getValue()); String subject = IETFUtils.valueToString(subjectCn.getFirst() .getValue()); if (StringUtils.isBlank(issuer) || issuer.equals(subject)) { List<Certificate> certificates = new ArrayList<>(); certificates.add(certificate); return certificates; } else { List<Certificate> certificates = buildCertChainList(issuer, store); certificates.add(certificate); return certificates; } } else { return new ArrayList<>(); } } catch (CertificateEncodingException | KeyStoreException e) { throw new KeystoreEditorException("Unable to build cert chain list.", e); } } @Override public void deletePrivateKey(String alias) { SecurityLogger.audit("Removing {} from System keystore.", alias); Path keyStoreFile = Paths.get(SecurityConstants.getKeystorePath()); if (!keyStoreFile.isAbsolute()) { Path ddfHomePath = Paths.get(System.getProperty("ddf.home")); keyStoreFile = Paths.get(ddfHomePath.toString(), keyStoreFile.toString()); } String keyStorePassword = SecurityConstants.getKeystorePassword(); deleteFromStore(alias, keyStoreFile.toString(), keyStorePassword, keyStore); } @Override public void deleteTrustedCertificate(String alias) { SecurityLogger.audit("Removing {} from System truststore.", alias); Path trustStoreFile = Paths.get(SecurityConstants.getTruststorePath()); if (!trustStoreFile.isAbsolute()) { Path ddfHomePath = Paths.get(System.getProperty("ddf.home")); trustStoreFile = Paths.get(ddfHomePath.toString(), trustStoreFile.toString()); } String trustStorePassword = SecurityConstants.getTruststorePassword(); deleteFromStore(alias, trustStoreFile.toString(), trustStorePassword, trustStore); } private synchronized void deleteFromStore(String alias, String path, String pass, KeyStore store) { if (alias == null) { throw new IllegalArgumentException("Alias cannot be null."); } File storeFile = new File(path); try (FileOutputStream fos = new FileOutputStream(storeFile)) { store.deleteEntry(alias); store.store(fos, pass.toCharArray()); } catch (KeyStoreException | IOException | CertificateException | NoSuchAlgorithmException e) { LOGGER.info("Unable to remove entry {} from store", alias, e); } } private void deleteAllSystemStoreEntries() throws KeyStoreException { Enumeration<String> aliases = keyStore.aliases(); while (aliases.hasMoreElements()) { deletePrivateKey(aliases.nextElement()); } aliases = trustStore.aliases(); while (aliases.hasMoreElements()) { deleteTrustedCertificate(aliases.nextElement()); } } private static class NonVerifyingSslSocketFactory extends SSLConnectionSocketFactory { NonVerifyingSslSocketFactory(SSLContext sslContext) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { super(sslContext, new NoopHostnameVerifier()); } } private static class NonVerifyingTrustManager implements X509TrustManager { public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return null; } } public static class KeystoreEditorException extends Exception { public KeystoreEditorException() { } public KeystoreEditorException(String message) { super(message); } public KeystoreEditorException(String message, Throwable cause) { super(message, cause); } public KeystoreEditorException(Throwable cause) { super(cause); } public KeystoreEditorException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } } }