001/*
002 * Copyright 2015-2024 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.dane.X509TrustManagerUtil;
015import org.minidns.dnssec.DnssecClient;
016import org.minidns.util.InetAddressUtil;
017
018import javax.net.ssl.SSLContext;
019import javax.net.ssl.SSLEngine;
020import javax.net.ssl.SSLSocket;
021import javax.net.ssl.TrustManager;
022import javax.net.ssl.X509ExtendedTrustManager;
023import javax.net.ssl.X509TrustManager;
024
025import java.net.Socket;
026import java.security.KeyManagementException;
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(X509TrustManagerUtil.getDefault());
054    }
055
056    public DaneExtendedTrustManager(DnssecClient client) {
057        this(client, X509TrustManagerUtil.getDefault());
058    }
059
060    public DaneExtendedTrustManager(X509TrustManager base) {
061        this(new DaneVerifier(), base);
062    }
063
064    public DaneExtendedTrustManager(DnssecClient client, X509TrustManager base) {
065        this(new DaneVerifier(client), base);
066    }
067
068    public DaneExtendedTrustManager(DaneVerifier verifier, X509TrustManager base) {
069        this.verifier = verifier;
070        this.base = base;
071    }
072
073    @Override
074    public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
075        if (base == null) {
076            LOGGER.warning("DaneExtendedTrustManager invalidly used for client certificate check and no fallback X509TrustManager specified");
077            return;
078        }
079
080        LOGGER.info("DaneExtendedTrustManager invalidly used for client certificate check forwarding request to fallback X509TrustManage");
081        if (base instanceof X509ExtendedTrustManager) {
082            ((X509ExtendedTrustManager) base).checkClientTrusted(chain, authType, socket);
083        } else {
084            base.checkClientTrusted(chain, authType);
085        }
086    }
087
088    @Override
089    public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
090        boolean verificationSuccessful = false;
091
092        if (socket instanceof SSLSocket) {
093            final SSLSocket sslSocket = (SSLSocket) socket;
094            final String hostname = sslSocket.getHandshakeSession().getPeerHost();
095
096            if (hostname == null) {
097                LOGGER.warning("Hostname returned by sslSocket.getHandshakeSession().getPeerHost() is null");
098            } else if (InetAddressUtil.isIpAddress(hostname)) {
099                LOGGER.warning(
100                        "Hostname returned by sslSocket.getHandshakeSession().getPeerHost() '" + hostname
101                                + "' is an IP address");
102            } else {
103                final int port = socket.getPort();
104                verificationSuccessful = verifier.verifyCertificateChain(chain, hostname, port);
105            }
106        } else {
107            throw new IllegalStateException("The provided socket '" + socket + "' is not of type SSLSocket");
108        }
109
110        if (verificationSuccessful) {
111            // Verification successful, no need to delegate to base trust manager.
112            return;
113        }
114
115        if (base instanceof X509ExtendedTrustManager) {
116            ((X509ExtendedTrustManager) base).checkServerTrusted(chain, authType, socket);
117        } else {
118            base.checkServerTrusted(chain, authType);
119        }
120    }
121
122    @Override
123    public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException {
124        if (base == null) {
125            LOGGER.warning("DaneExtendedTrustManager invalidly used for client certificate check and no fallback X509TrustManager specified");
126            return;
127        }
128
129        LOGGER.info("DaneExtendedTrustManager invalidly used for client certificate check, forwarding request to fallback X509TrustManage");
130        if (base instanceof X509ExtendedTrustManager) {
131            ((X509ExtendedTrustManager) base).checkClientTrusted(chain, authType, engine);
132        } else {
133            base.checkClientTrusted(chain, authType);
134        }
135    }
136
137    @Override
138    public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException {
139        if (verifier.verifyCertificateChain(chain, engine.getPeerHost(), engine.getPeerPort())) {
140            // Verification successful, no need to delegate to base trust manager.
141            return;
142        }
143
144        if (base instanceof X509ExtendedTrustManager) {
145            ((X509ExtendedTrustManager) base).checkServerTrusted(chain, authType, engine);
146        } else {
147            base.checkServerTrusted(chain, authType);
148        }
149    }
150
151    @Override
152    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
153        if (base == null) {
154            LOGGER.warning("DaneExtendedTrustManager invalidly used for client certificate check and no fallback X509TrustManager specified");
155            return;
156        }
157
158        LOGGER.info("DaneExtendedTrustManager invalidly used for client certificate check, forwarding request to fallback X509TrustManage");
159        base.checkClientTrusted(chain, authType);
160    }
161
162    @Override
163    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
164        LOGGER.info("DaneExtendedTrustManager cannot be used without hostname information, forwarding request to fallback X509TrustManage");
165        base.checkServerTrusted(chain, authType);
166    }
167
168    @Override
169    public X509Certificate[] getAcceptedIssuers() {
170        return base.getAcceptedIssuers();
171    }
172}