/* * Kontalk Java client * Copyright (C) 2016 Kontalk Devteam <devteam@kontalk.org> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.kontalk.util; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.UnrecoverableKeyException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Enumeration; import java.util.logging.Level; import java.util.logging.Logger; import org.kontalk.crypto.PGPUtils; /** * Utilities for SASL certificate validation. * @author Alexander Bikadorov {@literal <bikaejkb@mail.tu-berlin.de>} */ public class TrustUtils { private static final Logger LOGGER = Logger.getLogger(TrustUtils.class.getName()); private static final String TRUSTSTORE_FILE = "truststore.bks"; private static final String TRUSTSTORE_PASSWD = "123456"; private static TrustManager BLIND_TM = null; private static KeyStore MERGED_TS = null; public static SSLContext getCustomSSLContext(boolean validateCertificate) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException { return getCustomSSLContext(null, validateCertificate); } /** * Get a custom SSL context for secure server connections. The key store of * the context contains the private key and bridge certificate. The trust * manager contains system and own certificates or blindly accepts every * server certificate. */ public static SSLContext getCustomSSLContext( PrivateKey privateKey, X509Certificate bridgeCert, boolean validateCertificate) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException, KeyManagementException { // in-memory keystore KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); keystore.load(null, null); keystore.setKeyEntry("private", privateKey, new char[0], new Certificate[] { bridgeCert }); // key managers KeyManagerFactory kmFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmFactory.init(keystore, new char[0]); KeyManager[] km = kmFactory.getKeyManagers(); return getCustomSSLContext(km, validateCertificate); } private static SSLContext getCustomSSLContext(KeyManager[] km, boolean validateCertificate) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException { // trust managers TrustManager[] tm; if (validateCertificate) { // use modified truststore TrustManagerFactory tmFactory = TrustManagerFactory .getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmFactory.init(getTrustStore()); tm = tmFactory.getTrustManagers(); } else { // trust everything! tm = new TrustManager[] { getBlindTrustManager() }; } SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(km, tm, null); return ctx; } private static TrustManager getBlindTrustManager() { if (BLIND_TM == null) { BLIND_TM = new X509TrustManager() { @Override public X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } }; } return BLIND_TM; } /** * Load own trust store from file and system. * Return certificate store containing merged content from own keystore file * (containing certificates for StartCom) with system certificates (if any). */ private static KeyStore getTrustStore() throws KeyStoreException { if (MERGED_TS == null) { // note: there is no default truststore we can get from the JSSE MERGED_TS = KeyStore.getInstance(KeyStore.getDefaultType()); // load system keys String path = System.getProperty("javax.net.ssl.trustStore"); if (path == null) { path = System.getProperty("java.home") + File.separator + "lib" + File.separator + "security" + File.separator + "cacerts"; } try { MERGED_TS.load(new FileInputStream(path), null); } catch (IOException | NoSuchAlgorithmException | CertificateException ex) { LOGGER.log(Level.WARNING, "can't load system keys", ex); } // add own certs try { KeyStore myTS = KeyStore.getInstance("BKS", PGPUtils.PROVIDER); InputStream in = ClassLoader.getSystemResourceAsStream(TRUSTSTORE_FILE); myTS.load(in, TRUSTSTORE_PASSWD.toCharArray()); Enumeration<String> aliases = myTS.aliases(); while (aliases.hasMoreElements()) { String alias = aliases.nextElement(); Certificate cert = myTS.getCertificate(alias); if (MERGED_TS.containsAlias(alias)) LOGGER.info("overwriting system certificate: "+alias); MERGED_TS.setCertificateEntry(alias, cert); } } catch (CertificateException | IOException | KeyStoreException | NoSuchAlgorithmException | NoSuchProviderException ex) { LOGGER.log(Level.WARNING, "can't add certificates from own truststore", ex); } } //for (String alias : Collections.list(MERGED_TS.aliases())) // LOGGER.config("ts-alias: "+alias); return MERGED_TS; } }