diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslCertificateException.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslCertificateException.java new file mode 100644 index 0000000000..5d9bce0f0d --- /dev/null +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslCertificateException.java @@ -0,0 +1,80 @@ +/* + * Copyright 2016 The Netty Project + * + * The Netty Project 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 io.netty.handler.ssl; + +import org.apache.tomcat.jni.CertificateVerifier; + +import java.security.cert.CertificateException; + +/** + * A special {@link CertificateException} which allows to specify which error code is included in the + * SSL Record. This only work when {@link SslProvider#OPENSSL} is used. + */ +public final class OpenSslCertificateException extends CertificateException { + private static final long serialVersionUID = 5542675253797129798L; + + private final int errorCode; + + /** + * Construct a new exception with the + * error code. + */ + public OpenSslCertificateException(int errorCode) { + this((String) null, errorCode); + } + + /** + * Construct a new exception with the msg and + * error code . + */ + public OpenSslCertificateException(String msg, int errorCode) { + super(msg); + this.errorCode = checkErrorCode(errorCode); + } + + /** + * Construct a new exception with the msg, cause and + * error code . + */ + public OpenSslCertificateException(String message, Throwable cause, int errorCode) { + super(message, cause); + this.errorCode = checkErrorCode(errorCode); + } + + /** + * Construct a new exception with the cause and + * error code . + */ + public OpenSslCertificateException(Throwable cause, int errorCode) { + this(null, cause, errorCode); + } + + /** + * Return the error code to use. + */ + public int errorCode() { + return errorCode; + } + + private static int checkErrorCode(int errorCode) { + if (errorCode < CertificateVerifier.X509_V_OK || errorCode > CertificateVerifier.X509_V_ERR_DANE_NO_MATCH) { + throw new IllegalArgumentException("errorCode must be " + CertificateVerifier.X509_V_OK + " => " + + CertificateVerifier.X509_V_ERR_DANE_NO_MATCH + + ". See https://www.openssl.org/docs/manmaster/apps/verify.html ."); + } + return errorCode; + } +} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java index 8b3c7ea656..1dceb7885e 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslContext.java @@ -37,6 +37,9 @@ import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.X509TrustManager; import java.security.PrivateKey; import java.security.cert.Certificate; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.CertificateRevokedException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; @@ -449,19 +452,32 @@ public abstract class OpenSslContext extends SslContext { abstract class AbstractCertificateVerifier implements CertificateVerifier { @Override - public final boolean verify(long ssl, byte[][] chain, String auth) { + public final int verify(long ssl, byte[][] chain, String auth) { X509Certificate[] peerCerts = certificates(chain); final OpenSslEngine engine = engineMap.remove(ssl); try { verify(engine, peerCerts, auth); - return true; + return CertificateVerifier.X509_V_OK; } catch (Throwable cause) { logger.debug("verification of certificate failed", cause); SSLHandshakeException e = new SSLHandshakeException("General OpenSslEngine problem"); e.initCause(cause); engine.handshakeException = e; + + if (cause instanceof OpenSslCertificateException) { + return ((OpenSslCertificateException) cause).errorCode(); + } + if (cause instanceof CertificateExpiredException) { + return CertificateVerifier.X509_V_ERR_CERT_HAS_EXPIRED; + } + if (cause instanceof CertificateNotYetValidException) { + return CertificateVerifier.X509_V_ERR_CERT_NOT_YET_VALID; + } + if (PlatformDependent.javaVersion() >= 7 && cause instanceof CertificateRevokedException) { + return CertificateVerifier.X509_V_ERR_CERT_REVOKED; + } + return CertificateVerifier.X509_V_ERR_UNSPECIFIED; } - return false; } abstract void verify(OpenSslEngine engine, X509Certificate[] peerCerts, String auth) throws Exception; diff --git a/pom.xml b/pom.xml index db10f66126..de9bdc0321 100644 --- a/pom.xml +++ b/pom.xml @@ -223,7 +223,7 @@ fedora netty-tcnative - 1.1.33.Fork13 + 1.1.33.Fork14 ${os.detected.classifier} ${os.detected.name}-${os.detected.arch} @@ -676,6 +676,7 @@ javax.net.ssl.X509ExtendedTrustManager javax.net.ssl.SSLParameters java.security.AlgorithmConstraints + java.security.cert.CertificateRevokedException java.util.concurrent.ConcurrentLinkedDeque