blob: 1177089097a46833659775d54adc708f79b2b37f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package org.apache.servicecomb.foundation.ssl;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.Socket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.security.cert.CRL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.Set;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.X509ExtendedTrustManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 扩展TurstManager
*
*/
public class TrustManagerExt extends X509ExtendedTrustManager {
private static final Logger LOG = LoggerFactory.getLogger(TrustManagerExt.class);
private static final int WHITE_SIZE = 1024;
private final X509ExtendedTrustManager trustManager;
private final SSLOption option;
private final SSLCustom custom;
public TrustManagerExt(X509ExtendedTrustManager manager, SSLOption option,
SSLCustom custom) {
this.trustManager = manager;
this.option = option;
this.custom = custom;
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
if (!option.isAuthPeer()) {
return;
}
checkTrustedCustom(chain, null);
trustManager.checkClientTrusted(chain, authType);
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
if (!option.isAuthPeer()) {
return;
}
checkTrustedCustom(chain, null);
trustManager.checkServerTrusted(chain, authType);
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return trustManager.getAcceptedIssuers();
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType,
Socket socket) throws CertificateException {
if (!option.isAuthPeer()) {
return;
}
String ip = null;
if (socket != null && socket.isConnected()
&& socket instanceof SSLSocket) {
InetAddress inetAddress = socket.getInetAddress();
if (inetAddress != null) {
ip = inetAddress.getHostAddress();
}
}
checkTrustedCustom(chain, ip);
trustManager.checkClientTrusted(chain, authType, socket);
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType,
SSLEngine engine) throws CertificateException {
if (!option.isAuthPeer()) {
return;
}
String ip = null;
if (engine != null) {
SSLSession session = engine.getHandshakeSession();
ip = session.getPeerHost();
}
checkTrustedCustom(chain, ip);
trustManager.checkClientTrusted(chain, authType, engine);
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType,
Socket socket) throws CertificateException {
if (!option.isAuthPeer()) {
return;
}
String ip = null;
if (socket != null && socket.isConnected()
&& socket instanceof SSLSocket) {
InetAddress inetAddress = socket.getInetAddress();
if (inetAddress != null) {
ip = inetAddress.getHostAddress();
}
}
checkTrustedCustom(chain, ip);
trustManager.checkServerTrusted(chain, authType, socket);
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType,
SSLEngine engine) throws CertificateException {
if (!option.isAuthPeer()) {
return;
}
String ip = null;
if (engine != null) {
SSLSession session = engine.getHandshakeSession();
ip = session.getPeerHost();
}
checkTrustedCustom(chain, ip);
trustManager.checkServerTrusted(chain, authType, engine);
}
private void checkTrustedCustom(X509Certificate[] chain, String ip) throws CertificateException {
checkCNHost(chain, ip);
checkCNWhite(chain);
checkCRL(chain);
}
// ? : learn java default / apache CN check
private void checkCNHost(X509Certificate[] chain, String ip) throws CertificateException {
if (option.isCheckCNHost()) {
X509Certificate owner = CertificateUtil.findOwner(chain);
Set<String> cns = CertificateUtil.getCN(owner);
String ipTmp = ip == null ? custom.getHost() : ip;
// 从本机来的请求, 只要CN与本机的任何一个IP地址匹配即可
if ("127.0.0.1".equals(ipTmp)) {
try {
Enumeration<NetworkInterface> interfaces =
NetworkInterface.getNetworkInterfaces();
if (interfaces != null) {
while (interfaces.hasMoreElements()) {
NetworkInterface nif = interfaces.nextElement();
Enumeration<InetAddress> ias = nif.getInetAddresses();
while (ias.hasMoreElements()) {
InetAddress ia = ias.nextElement();
String local = ia.getHostAddress();
if (cnValid(cns, local)) {
return;
}
}
}
}
} catch (SocketException e) {
throw new CertificateException("Get local adrress fail.");
}
} else if (cnValid(cns, ipTmp)) {
return;
}
LOG.error("CN does not match IP: e=" + cns
+ ",t=" + ip);
throw new CertificateException("CN does not match IP: e=" + cns
+ ",t=" + ip);
}
}
private boolean cnValid(Set<String> certsCN, String srcCN) {
for (String cert : certsCN) {
if (cert.equals(srcCN)) {
return true;
}
}
return false;
}
private void checkCNWhite(X509Certificate[] chain) throws CertificateException {
if (option.isCheckCNWhite()) {
FileInputStream fis = null;
InputStreamReader reader = null;
try {
String white = option.getCheckCNWhiteFile();
white = custom.getFullPath(white);
fis = new FileInputStream(white);
reader = new InputStreamReader(fis, StandardCharsets.UTF_8);
char[] buffer = new char[WHITE_SIZE];
int len = reader.read(buffer);
String[] cns = new String(buffer, 0, len).split("\\s+");
X509Certificate owner = CertificateUtil.findOwner(chain);
Set<String> certCN = CertificateUtil.getCN(owner);
for (String c : cns) {
if (cnValid(certCN, c)) {
return;
}
}
} catch (FileNotFoundException e) {
throw new CertificateException(
"CN does not match white. no white file.");
} catch (IOException e) {
throw new CertificateException(
"CN does not match white. can not read file.");
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
ignore();
}
try {
if (fis != null) {
fis.close();
}
} catch (IOException e) {
ignore();
}
}
LOG.error("CN does not match white.");
throw new CertificateException("CN does not match white.");
}
}
private void checkCRL(X509Certificate[] chain) throws CertificateException {
String crl = option.getCrl();
crl = custom.getFullPath(crl);
File file = new File(crl);
if (!file.exists()) {
return;
}
CRL[] crls = KeyStoreUtil.createCRL(crl);
X509Certificate owner = CertificateUtil.findOwner(chain);
for (CRL c : crls) {
if (c.isRevoked(owner)) {
LOG.error("certificate revoked");
throw new CertificateException("certificate revoked");
}
}
}
private static void ignore() {
}
}