Skip to content

Commit

Permalink
Add a New ConfigurableX509TrustManager Class
Browse files Browse the repository at this point in the history
  • Loading branch information
ZhenLian committed Mar 5, 2020
1 parent e29561f commit 0914bcb
Show file tree
Hide file tree
Showing 3 changed files with 603 additions and 0 deletions.
134 changes: 134 additions & 0 deletions netty/src/main/java/io/grpc/netty/ConfigurableX509TrustManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* Copyright 2020 The gRPC Authors
*
* Licensed 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 io.grpc.netty;

import io.grpc.netty.TlsOptions.VerificationAuthType;
import java.net.Socket;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedTrustManager;

public class ConfigurableX509TrustManager extends X509ExtendedTrustManager {

private TlsOptions tlsOptions;

public ConfigurableX509TrustManager(TlsOptions tlsOptions) {
this.tlsOptions = tlsOptions;
}

@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket)
throws CertificateException {

}

@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine)
throws CertificateException {
checkTrusted(x509Certificates, s, sslEngine, false);
}

@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s)
throws CertificateException {

}

@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket)
throws CertificateException {

}

@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine)
throws CertificateException {
checkTrusted(x509Certificates, s, sslEngine, true);
}

@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s)
throws CertificateException {

}

@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}

private void checkTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine,
boolean isClient) throws CertificateException {
VerificationAuthType authType = this.tlsOptions.getVerificationAuthType();
if (authType == VerificationAuthType.CertificateAndHostNameVerification
|| authType == VerificationAuthType.CertificateVerification) {
if (x509Certificates == null || x509Certificates.length == 0) {
throw new CertificateException(
"Client side requires certificate but got null or empty certificates");
}
KeyStore ks;
try {
ks = this.tlsOptions.getTrustedCerts();
} catch (Exception e) {
throw new CertificateException("Function getTrustedCerts fails, error: " + e.getMessage());
}
X509ExtendedTrustManager delegateManager = null;
try {
final TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
TrustManager[] tms = tmf.getTrustManagers();
// Iterate over the returned trust managers, looking for an instance of X509TrustManager.
// If found, use that as the delegate trust manager.
for (int i = 0; i < tms.length; i++) {
if (tms[i] instanceof X509ExtendedTrustManager) {
delegateManager = (X509ExtendedTrustManager) tms[i];
break;
}
}
if (delegateManager == null) {
throw new CertificateException(
"Instance delegateX509TrustManager is null. Failed to initialize");
}
} catch (Exception e) {
throw new CertificateException("Failed to initialize delegateX509TrustManager, error: "
+ e.getMessage());
}
if (isClient) {
String algorithm = authType == VerificationAuthType.CertificateAndHostNameVerification
? "HTTPS" : "";
SSLParameters sslParams = sslEngine.getSSLParameters();
sslParams.setEndpointIdentificationAlgorithm(algorithm);
sslEngine.setSSLParameters(sslParams);
delegateManager.checkServerTrusted(x509Certificates, s, sslEngine);
} else {
delegateManager.checkClientTrusted(x509Certificates, s, sslEngine);
}
}
// Perform custom check
try {
this.tlsOptions.verifyPeerCertificate(x509Certificates, s, sslEngine);
} catch (Exception e) {
throw new CertificateException("Custom authorization check fails, error: " + e.getMessage());
}
}
}
62 changes: 62 additions & 0 deletions netty/src/main/java/io/grpc/netty/TlsOptions.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright 2020 The gRPC Authors
*
* Licensed 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 io.grpc.netty;

import java.security.KeyStore;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLEngine;

// TlsOptions contains different options users could choose. In a nutshell, it provides three main
// features users could customize:
// 1. choose different levels of peer verification by specifying |VerificationAuthType|
// 2. provide custom peer verification check by inheriting |verifyPeerCertificate|
// 3. change the trust CA certificate bundle by inheriting |getTrustedCerts|
public abstract class TlsOptions {
// VerificationAuthType contains set of verification levels users can choose to customize
// their checks against its peer.
// Note we don't have hostname check on server side. Choosing CertificateAndHostNameVerification
// has the same effect as choosing CertificateVerification on server side, in terms of peer
// endpoint check.
public enum VerificationAuthType {
// Default option: performs certificate verification and hostname verification.
CertificateAndHostNameVerification,
// Performs certificate verification, but skips hostname verification.
// Users are responsible for verifying peer's identity via custom check callback.
CertificateVerification,
// Skips both certificate and hostname verification.
// Users are responsible for verifying peer's identity and peer's certificate via custom
// check callback.
SkipAllVerification,
}

private VerificationAuthType verificationType;

public TlsOptions(VerificationAuthType verificationType) {
this.verificationType = verificationType;
}

public VerificationAuthType getVerificationAuthType() {
return this.verificationType;
}

// used to perform custom peer authorization checking
abstract void verifyPeerCertificate(X509Certificate[] peerCertChain, String authType,
SSLEngine engine) throws Exception;

// used to perform trust CA certificates reloading
abstract KeyStore getTrustedCerts() throws Exception;
}
Loading

0 comments on commit 0914bcb

Please sign in to comment.