001/*
002 * Copyright 2015-2020 the original author or authors
003 *
004 * This software is licensed under the Apache License, Version 2.0,
005 * the GNU Lesser General Public License version 2 or later ("LGPL")
006 * and the WTFPL.
007 * You may choose either license to govern your use of this software only
008 * upon the condition that you accept all of the terms of either
009 * the Apache License 2.0, the LGPL 2.1+ or the WTFPL.
010 */
011package org.minidns.dane.java7;
012
013import org.minidns.dane.DaneVerifier;
014import org.minidns.dnssec.DnssecClient;
015
016import javax.net.ssl.SSLContext;
017import javax.net.ssl.SSLEngine;
018import javax.net.ssl.TrustManager;
019import javax.net.ssl.TrustManagerFactory;
020import javax.net.ssl.X509ExtendedTrustManager;
021import javax.net.ssl.X509TrustManager;
022
023import java.net.Socket;
024import java.security.KeyManagementException;
025import java.security.KeyStore;
026import java.security.KeyStoreException;
027import java.security.NoSuchAlgorithmException;
028import java.security.cert.CertificateException;
029import java.security.cert.X509Certificate;
030import java.util.logging.Logger;
031
032public class DaneExtendedTrustManager extends X509ExtendedTrustManager {
033    private static final Logger LOGGER = Logger.getLogger(DaneExtendedTrustManager.class.getName());
034
035    private final X509TrustManager base;
036    private final DaneVerifier verifier;
037
038    public static void inject() {
039        inject(new DaneExtendedTrustManager());
040    }
041
042    public static void inject(DaneExtendedTrustManager trustManager) {
043        try {
044            SSLContext sslContext = SSLContext.getInstance("TLS");
045            sslContext.init(null, new TrustManager[] {trustManager}, null);
046            SSLContext.setDefault(sslContext);
047        } catch (NoSuchAlgorithmException | KeyManagementException e) {
048            throw new RuntimeException(e);
049        }
050    }
051
052    public DaneExtendedTrustManager() {
053        this(getDefaultTrustManager());
054    }
055
056    public DaneExtendedTrustManager(DnssecClient client) {
057        this(client, getDefaultTrustManager());
058    }
059
060    private static X509TrustManager getDefaultTrustManager() {
061        try {
062            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
063            tmf.init((KeyStore) null);
064            for (TrustManager trustManager : tmf.getTrustManagers()) {
065                if (trustManager instanceof X509TrustManager)
066                    return (X509TrustManager) trustManager;
067            }
068        } catch (NoSuchAlgorithmException | KeyStoreException e) {
069            throw new RuntimeException("X.509 not supported.", e);
070        }
071        return null;
072    }
073
074    public DaneExtendedTrustManager(X509TrustManager base) {
075        this(new DaneVerifier(), base);
076    }
077
078    public DaneExtendedTrustManager(DnssecClient client, X509TrustManager base) {
079        this(new DaneVerifier(client), base);
080    }
081
082    public DaneExtendedTrustManager(DaneVerifier verifier, X509TrustManager base) {
083        this.verifier = verifier;
084        this.base = base;
085    }
086
087    @Override
088    public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
089        if (base == null) {
090            LOGGER.warning("DaneExtendedTrustManager invalidly used for client certificate check and no fallback X509TrustManager specified");
091        } else {
092            LOGGER.info("DaneExtendedTrustManager invalidly used for client certificate check forwarding request to fallback X509TrustManage");
093            if (base instanceof X509ExtendedTrustManager) {
094                ((X509ExtendedTrustManager) base).checkClientTrusted(chain, authType, socket);
095            } else {
096                base.checkClientTrusted(chain, authType);
097            }
098        }
099    }
100
101    @Override
102    public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
103        if (!verifier.verifyCertificateChain(chain, socket.getInetAddress().getHostName(), socket.getPort())) {
104            if (base instanceof X509ExtendedTrustManager) {
105                ((X509ExtendedTrustManager) base).checkServerTrusted(chain, authType, socket);
106            } else {
107                base.checkClientTrusted(chain, authType);
108            }
109        }
110    }
111
112    @Override
113    public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException {
114        if (base == null) {
115            LOGGER.warning("DaneExtendedTrustManager invalidly used for client certificate check and no fallback X509TrustManager specified");
116        } else {
117            LOGGER.info("DaneExtendedTrustManager invalidly used for client certificate check, forwarding request to fallback X509TrustManage");
118            if (base instanceof X509ExtendedTrustManager) {
119                ((X509ExtendedTrustManager) base).checkClientTrusted(chain, authType, engine);
120            } else {
121                base.checkClientTrusted(chain, authType);
122            }
123        }
124    }
125
126    @Override
127    public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException {
128        if (!verifier.verifyCertificateChain(chain, engine.getPeerHost(), engine.getPeerPort())) {
129            if (base instanceof X509ExtendedTrustManager) {
130                ((X509ExtendedTrustManager) base).checkServerTrusted(chain, authType, engine);
131            } else {
132                base.checkClientTrusted(chain, authType);
133            }
134        }
135    }
136
137    @Override
138    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
139        if (base == null) {
140            LOGGER.warning("DaneExtendedTrustManager invalidly used for client certificate check and no fallback X509TrustManager specified");
141        } else {
142            LOGGER.info("DaneExtendedTrustManager invalidly used for client certificate check, forwarding request to fallback X509TrustManage");
143            base.checkClientTrusted(chain, authType);
144        }
145    }
146
147    @Override
148    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
149        LOGGER.info("DaneExtendedTrustManager cannot be used without hostname information, forwarding request to fallback X509TrustManage");
150        base.checkServerTrusted(chain, authType);
151    }
152
153    @Override
154    public X509Certificate[] getAcceptedIssuers() {
155        return base.getAcceptedIssuers();
156    }
157}