如何在 java TLS 服务器上启用 OCSP 装订?
How to enable OCSP stapling on java TLS server?
此 post 是跨posted,因此请务必检查更新 in coderanch。
我很难在我的 client/server 应用程序上实施 OCSP 吊销检查,我设法让客户端 OCSP 工作,我用 openssl 实施了我自己的 OCSP 响应器,我正在检查我的签名的证书自己的 CA.
尝试从服务器检查它们时出现问题。我按照 Standard Edition Security Developer’s Guide 上的说明操作,更确切地说是这样:
static class ServerParameters {
boolean enabled = true;
int cacheSize = 256;
int cacheLifetime = 3600;
int respTimeout = 5000;
String respUri = "http://localhost:9999";
boolean respOverride = false;
boolean ignoreExts = false;
String[] protocols = new String[]{ "TLSv1.2" };
String[] ciphers = null;
ServerParameters() { }
}
... 在我调用 SSLContext.getInstance("TSL") 之前:
ServerParameters servParams = new ServerParameters();
System.setProperty("jdk.tls.server.enableStatusRequestExtension",
Boolean.toString(servParams.enabled));
System.setProperty("jdk.tls.stapling.cacheSize",
Integer.toString(servParams.cacheSize));
System.setProperty("jdk.tls.stapling.cacheLifetime",
Integer.toString(servParams.cacheLifetime));
System.setProperty("jdk.tls.stapling.responseTimeout",
Integer.toString(servParams.respTimeout));
System.setProperty("jdk.tls.stapling.responderURI", servParams.respUri);
System.setProperty("jdk.tls.stapling.responderOverride",
Boolean.toString(servParams.respOverride));
System.setProperty("jdk.tls.stapling.ignoreExtensions",
Boolean.toString(servParams.ignoreExts));
在握手过程中检查数据包,我意识到客户端正确添加了 status_request 扩展,但是服务器没有发送 CertificateStatusMessage,也没有向 OCSP 响应者发送请求。
我也将响应者 URL 添加到证书中。
我尝试使用 TLS 1.2 和 1.3,Java 11 和 15。没有成功。
它应该如何工作:
Wireshark 的外观:
为 wireshark 正确配置的端口将消息显示为 tls:
我不知道我做错了什么,文档看起来很简单,但我做不到。
Wireshark 生成的包含握手数据包信息的文本文件:file.txt
客户问候status_request:
Extension: status_request (len=5)
Type: status_request (5)
Length: 5
Certificate Status Type: OCSP (1)
Responder ID list Length: 0
Request Extensions Length: 0
可以看出,客户端问候包括状态请求,但是服务器(如果客户端有的话应该包括一个)没有写扩展,就像它没有意识到 status_request.
添加:我用于测试的完整代码,这是一个修改,删除了 ocsp 响应程序的启动并创建了 this code 的证书:
/*
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// SunJSSE does not support dynamic system properties, no way to re-use
// system properties in samevm/agentvm mode.
/*
* @test
* @bug 8046321 8153829
* @summary OCSP Stapling for TLS
* @library ../../../../java/security/testlibrary
* @build CertificateBuilder SimpleOCSPServer
* @run main/othervm SSLSocketWithStapling
*/
import java.io.*;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.Socket;
import java.net.ServerSocket;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import javax.net.ssl.*;
import java.security.KeyStore;
import java.security.PublicKey;
import java.security.Security;
import java.security.cert.CertPathValidator;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertPathValidatorException.BasicReason;
import java.security.cert.Certificate;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
import java.security.cert.PKIXRevocationChecker;
import java.security.cert.PKIXRevocationChecker.Option;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
public class SSLSocketWithStapling {
/*
* =============================================================
* Set the various variables needed for the tests, then
* specify what tests to run on each side.
*/
// Turn on TLS debugging
static final boolean debug = false;
/*
* Should we run the client or server in a separate thread?
* Both sides can throw exceptions, but do you have a preference
* as to which side should be the main thread.
*/
static boolean separateServerThread = true;
Thread clientThread = null;
Thread serverThread = null;
static String passwd = "serverpass";
/*
* Is the server ready to serve?
*/
volatile static boolean serverReady = false;
volatile int serverPort = 443;
volatile Exception serverException = null;
volatile Exception clientException = null;
// PKI components we will need for this test
static KeyStore serverKeystore; // SSL Server Keystore
static KeyStore trustStore; // SSL Client trust store
// Extra configuration parameters and constants
static final String[] TLS13ONLY = new String[] { "TLSv1.3" };
static final String[] TLS12MAX =
new String[] { "TLSv1.2", "TLSv1.1", "TLSv1" };
/*
* If the client or server is doing some kind of object creation
* that the other side depends on, and that thread prematurely
* exits, you may experience a hang. The test harness will
* terminate all hung threads after its timeout has expired,
* currently 3 minutes by default, but you might try to be
* smart about it....
*/
public static void main(String[] args) throws Exception {
if (debug) {
System.setProperty("javax.net.debug", "ssl:handshake");
}
try {
String keystoreServerPath ="/Users/lexy/Desktop/Clases/Seguridad/almacenes/keystoreServidor.jceks";
String trustClientPath = "/Users/lexy/Desktop/Clases/Seguridad/almacenes/truststoreClient.jceks";
System.setProperty("jdk.security.allowNonCaAnchor", "true" );
System.setProperty("javax.net.ssl.trustStore", trustClientPath);
System.setProperty("javax.net.ssl.trustStoreType", "JCEKS");
System.setProperty("javax.net.ssl.trustStorePassword", "clientpass");
serverKeystore = KeyStore.getInstance("JCEKS");
//System.out.println(passwd_key);
serverKeystore.load(new FileInputStream(keystoreServerPath),"serverpass".toCharArray());
trustStore = KeyStore.getInstance("JCEKS");
//trustedStore.load(new FileInputStream("C:\Users\usuario\Desktop\alamcenes/clientTrustedCerts.jks"), "clientpass".toCharArray());
trustStore.load(new FileInputStream(trustClientPath), "clientpass".toCharArray()); //supuestamente no hay que poner contrase�a es la misma pero no se deberia i dont know
testPKIXParametersRevEnabled(false);
testPKIXParametersRevEnabled(true);
} finally {
}
}
/**
* Do a basic connection using PKIXParameters with revocation checking
* enabled and client-side OCSP disabled. It will only pass if all
* stapled responses are present, valid and have a GOOD status.
*/
static class ClientParameters {
boolean enabled = true;
PKIXBuilderParameters pkixParams = null;
PKIXRevocationChecker revChecker = null;
String[] protocols = null;
String[] ciphers = null;
ClientParameters() { }
}
static class ServerParameters {
boolean enabled = true;
int cacheSize = 256;
int cacheLifetime = 3600;
int respTimeout = 5000;
String respUri = "http://localhost:9999";
boolean respOverride = false;
boolean ignoreExts = false;
String[] protocols = null;
String[] ciphers = null;
ServerParameters() { }
}
static void testPKIXParametersRevEnabled(boolean isTls13) throws Exception {
ClientParameters cliParams = new ClientParameters();
ServerParameters servParams = new ServerParameters();
if (isTls13) {
cliParams.protocols = TLS13ONLY;
servParams.protocols = TLS13ONLY;
} else {
cliParams.protocols = TLS12MAX;
servParams.protocols = TLS12MAX;
}
serverReady = false;
System.out.println("=====================================");
System.out.println("Stapling enabled, PKIXParameters with");
System.out.println("Revocation checking enabled ");
System.out.println("=====================================");
cliParams.pkixParams = new PKIXBuilderParameters(trustStore,
new X509CertSelector());
cliParams.pkixParams.setRevocationEnabled(true);
Security.setProperty("ocsp.enable", "false");
cliParams.enabled=true;
servParams.enabled=true;
servParams.respUri="http://localhost:9999";
SSLSocketWithStapling sslTest = new SSLSocketWithStapling(cliParams,
servParams);
TestResult tr = sslTest.getResult();
if (tr.clientExc != null) {
throw tr.clientExc;
} else if (tr.serverExc != null) {
throw tr.serverExc;
}
System.out.println(" PASS");
System.out.println("=====================================\n");
}
/*
* Define the server side of the test.
*
* If the server prematurely exits, serverReady will be set to true
* to avoid infinite hangs.
*/
void doServerSide(ServerParameters servParams) throws Exception {
// Selectively enable or disable the feature
System.setProperty("jdk.tls.server.enableStatusRequestExtension",
Boolean.toString(servParams.enabled));
// Set all the other operating parameters
System.setProperty("jdk.tls.stapling.cacheSize",
Integer.toString(servParams.cacheSize));
System.setProperty("jdk.tls.stapling.cacheLifetime",
Integer.toString(servParams.cacheLifetime));
System.setProperty("jdk.tls.stapling.responseTimeout",
Integer.toString(servParams.respTimeout));
System.setProperty("jdk.tls.stapling.responderURI", servParams.respUri);
System.setProperty("jdk.tls.stapling.responderOverride",
Boolean.toString(servParams.respOverride));
System.setProperty("jdk.tls.stapling.ignoreExtensions",
Boolean.toString(servParams.ignoreExts));
// Set keystores and trust stores for the server
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(serverKeystore, passwd.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(trustStore);
SSLContext sslc = SSLContext.getInstance("TLS");
sslc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
SSLServerSocketFactory sslssf = new CustomizedServerSocketFactory(sslc,
servParams.protocols, servParams.ciphers);
try (SSLServerSocket sslServerSocket =
(SSLServerSocket) sslssf.createServerSocket(serverPort)) {
serverPort = sslServerSocket.getLocalPort();
/*
* Signal Client, we're ready for his connect.
*/
serverReady = true;
try (SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
InputStream sslIS = sslSocket.getInputStream();
OutputStream sslOS = sslSocket.getOutputStream()) {
int numberIn = sslIS.read();
int numberSent = 85;
log("Server received number: " + numberIn);
sslOS.write(numberSent);
sslOS.flush();
log("Server sent number: " + numberSent);
}
}
}
/*
* Define the client side of the test.
*
* If the server prematurely exits, serverReady will be set to true
* to avoid infinite hangs.
*/
void doClientSide(ClientParameters cliParams) throws Exception {
// Wait 5 seconds for server ready
for (int i = 0; (i < 100 && !serverReady); i++) {
Thread.sleep(50);
}
if (!serverReady) {
throw new RuntimeException("Server not ready yet");
}
// Selectively enable or disable the feature
System.setProperty("jdk.tls.client.enableStatusRequestExtension",
Boolean.toString(cliParams.enabled));
// Create the Trust Manager Factory using the PKIX variant
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
// If we have a customized pkixParameters then use it
if (cliParams.pkixParams != null) {
// LIf we have a customized PKIXRevocationChecker, add
// it to the PKIXBuilderParameters.
if (cliParams.revChecker != null) {
cliParams.pkixParams.addCertPathChecker(cliParams.revChecker);
}
ManagerFactoryParameters trustParams =
new CertPathTrustManagerParameters(cliParams.pkixParams);
tmf.init(trustParams);
} else {
tmf.init(trustStore);
}
SSLContext sslc = SSLContext.getInstance("TLS");
sslc.init(null, tmf.getTrustManagers(), null);
SSLSocketFactory sslsf = new CustomizedSocketFactory(sslc,
cliParams.protocols, cliParams.ciphers);
try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket("localhost",
serverPort);
InputStream sslIS = sslSocket.getInputStream();
OutputStream sslOS = sslSocket.getOutputStream()) {
int numberSent = 80;
sslOS.write(numberSent);
sslOS.flush();
log("Client sent number: " + numberSent);
int numberIn = sslIS.read();
log("Client received number:" + numberIn);
}
}
/*
* Primary constructor, used to drive remainder of the test.
*
* Fork off the other side, then do your work.
*/
SSLSocketWithStapling(ClientParameters cliParams,
ServerParameters servParams) throws Exception {
Exception startException = null;
try {
if (separateServerThread) {
startServer(servParams, true);
startClient(cliParams, false);
} else {
startClient(cliParams, true);
startServer(servParams, false);
}
} catch (Exception e) {
startException = e;
}
/*
* Wait for other side to close down.
*/
if (separateServerThread) {
if (serverThread != null) {
serverThread.join();
}
} else {
if (clientThread != null) {
clientThread.join();
}
}
}
TestResult getResult() {
TestResult tr = new TestResult();
tr.clientExc = clientException;
tr.serverExc = serverException;
return tr;
}
void startServer(ServerParameters servParams, boolean newThread)
throws Exception {
if (newThread) {
serverThread = new Thread() {
public void run() {
try {
doServerSide(servParams);
} catch (Exception e) {
/*
* Our server thread just died.
*
* Release the client, if not active already...
*/
System.err.println("Server died...");
e.printStackTrace(System.err);
serverReady = true;
serverException = e;
}
}
};
serverThread.start();
} else {
try {
doServerSide(servParams);
} catch (Exception e) {
serverException = e;
} finally {
serverReady = true;
}
}
}
void startClient(ClientParameters cliParams, boolean newThread)
throws Exception {
if (newThread) {
clientThread = new Thread() {
public void run() {
try {
doClientSide(cliParams);
} catch (Exception e) {
/*
* Our client thread just died.
*/
System.err.println("Client died...");
clientException = e;
}
}
};
clientThread.start();
} else {
try {
doClientSide(cliParams);
} catch (Exception e) {
clientException = e;
}
}
}
/**
* Log a message on stdout
*
* @param message The message to log
*/
private static void log(String message) {
if (debug) {
System.out.println(message);
}
}
// The following two classes are Simple nested class to group a handful
// of configuration parameters used before starting a client or server.
// We'll just access the data members directly for convenience.
static class CustomizedSocketFactory extends SSLSocketFactory {
final SSLContext sslc;
final String[] protocols;
final String[] cipherSuites;
CustomizedSocketFactory(SSLContext ctx, String[] prots, String[] suites)
throws GeneralSecurityException {
super();
sslc = (ctx != null) ? ctx : SSLContext.getDefault();
protocols = prots;
cipherSuites = suites;
}
@Override
public Socket createSocket(Socket s, String host, int port,
boolean autoClose) throws IOException {
Socket sock = sslc.getSocketFactory().createSocket(s, host, port,
autoClose);
customizeSocket(sock);
return sock;
}
@Override
public Socket createSocket(InetAddress host, int port)
throws IOException {
Socket sock = sslc.getSocketFactory().createSocket(host, port);
customizeSocket(sock);
return sock;
}
@Override
public Socket createSocket(InetAddress host, int port,
InetAddress localAddress, int localPort) throws IOException {
Socket sock = sslc.getSocketFactory().createSocket(host, port,
localAddress, localPort);
customizeSocket(sock);
return sock;
}
@Override
public Socket createSocket(String host, int port)
throws IOException {
Socket sock = sslc.getSocketFactory().createSocket(host, port);
customizeSocket(sock);
return sock;
}
@Override
public Socket createSocket(String host, int port,
InetAddress localAddress, int localPort)
throws IOException {
Socket sock = sslc.getSocketFactory().createSocket(host, port,
localAddress, localPort);
customizeSocket(sock);
return sock;
}
@Override
public String[] getDefaultCipherSuites() {
return sslc.getDefaultSSLParameters().getCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return sslc.getSupportedSSLParameters().getCipherSuites();
}
private void customizeSocket(Socket sock) {
if (sock instanceof SSLSocket) {
if (protocols != null) {
((SSLSocket)sock).setEnabledProtocols(protocols);
}
if (cipherSuites != null) {
((SSLSocket)sock).setEnabledCipherSuites(cipherSuites);
}
}
}
}
static class CustomizedServerSocketFactory extends SSLServerSocketFactory {
final SSLContext sslc;
final String[] protocols;
final String[] cipherSuites;
CustomizedServerSocketFactory(SSLContext ctx, String[] prots, String[] suites)
throws GeneralSecurityException {
super();
sslc = (ctx != null) ? ctx : SSLContext.getDefault();
protocols = prots;
cipherSuites = suites;
}
@Override
public ServerSocket createServerSocket(int port) throws IOException {
ServerSocket sock =
sslc.getServerSocketFactory().createServerSocket(port);
customizeSocket(sock);
return sock;
}
@Override
public ServerSocket createServerSocket(int port, int backlog)
throws IOException {
ServerSocket sock =
sslc.getServerSocketFactory().createServerSocket(port,
backlog);
customizeSocket(sock);
return sock;
}
@Override
public ServerSocket createServerSocket(int port, int backlog,
InetAddress ifAddress) throws IOException {
ServerSocket sock =
sslc.getServerSocketFactory().createServerSocket(port,
backlog, ifAddress);
customizeSocket(sock);
return sock;
}
@Override
public String[] getDefaultCipherSuites() {
return sslc.getDefaultSSLParameters().getCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return sslc.getSupportedSSLParameters().getCipherSuites();
}
private void customizeSocket(ServerSocket sock) {
if (sock instanceof SSLServerSocket) {
if (protocols != null) {
((SSLServerSocket)sock).setEnabledProtocols(protocols);
}
if (cipherSuites != null) {
((SSLServerSocket)sock).setEnabledCipherSuites(cipherSuites);
}
}
}
}
static class TestResult {
Exception serverExc = null;
Exception clientExc = null;
}
}
问题已解决:
在我的例子中,服务器装订不工作导致服务器证书配置错误。
服务器证书必须链接到根 CA 证书,而我的是单独的。另外,我在证书上指定了 authorityInfoAccess 扩展。
所以:
我用于签署 ssl 证书的 openssl 配置文件如下所示(请参阅带有 OCSP URI 的 authorityInfoAccess):
[ auth_cert ]
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = critical, serverAuth,clientAuth
authorityInfoAccess = OCSP;URI:http://localhost:9999
还要确保您的 ssl 证书与 CA 或 CA 和中间证书链接在一起,具体取决于您的设置。
如果是per format,就拼接起来,在linux中用cat工具,我用的命令是:
cat ca.crt.pem >> server/serverauth.crt.pem
有关我如何实现的更多信息,请查看我在此 coderanch thread 上的帖子。
此 post 是跨posted,因此请务必检查更新 in coderanch。
我很难在我的 client/server 应用程序上实施 OCSP 吊销检查,我设法让客户端 OCSP 工作,我用 openssl 实施了我自己的 OCSP 响应器,我正在检查我的签名的证书自己的 CA.
尝试从服务器检查它们时出现问题。我按照 Standard Edition Security Developer’s Guide 上的说明操作,更确切地说是这样:
static class ServerParameters {
boolean enabled = true;
int cacheSize = 256;
int cacheLifetime = 3600;
int respTimeout = 5000;
String respUri = "http://localhost:9999";
boolean respOverride = false;
boolean ignoreExts = false;
String[] protocols = new String[]{ "TLSv1.2" };
String[] ciphers = null;
ServerParameters() { }
}
... 在我调用 SSLContext.getInstance("TSL") 之前:
ServerParameters servParams = new ServerParameters();
System.setProperty("jdk.tls.server.enableStatusRequestExtension",
Boolean.toString(servParams.enabled));
System.setProperty("jdk.tls.stapling.cacheSize",
Integer.toString(servParams.cacheSize));
System.setProperty("jdk.tls.stapling.cacheLifetime",
Integer.toString(servParams.cacheLifetime));
System.setProperty("jdk.tls.stapling.responseTimeout",
Integer.toString(servParams.respTimeout));
System.setProperty("jdk.tls.stapling.responderURI", servParams.respUri);
System.setProperty("jdk.tls.stapling.responderOverride",
Boolean.toString(servParams.respOverride));
System.setProperty("jdk.tls.stapling.ignoreExtensions",
Boolean.toString(servParams.ignoreExts));
在握手过程中检查数据包,我意识到客户端正确添加了 status_request 扩展,但是服务器没有发送 CertificateStatusMessage,也没有向 OCSP 响应者发送请求。
我也将响应者 URL 添加到证书中。 我尝试使用 TLS 1.2 和 1.3,Java 11 和 15。没有成功。
它应该如何工作:
Wireshark 的外观:
为 wireshark 正确配置的端口将消息显示为 tls:
我不知道我做错了什么,文档看起来很简单,但我做不到。
Wireshark 生成的包含握手数据包信息的文本文件:file.txt
客户问候status_request:
Extension: status_request (len=5) Type: status_request (5) Length: 5 Certificate Status Type: OCSP (1) Responder ID list Length: 0 Request Extensions Length: 0
可以看出,客户端问候包括状态请求,但是服务器(如果客户端有的话应该包括一个)没有写扩展,就像它没有意识到 status_request.
添加:我用于测试的完整代码,这是一个修改,删除了 ocsp 响应程序的启动并创建了 this code 的证书:
/*
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// SunJSSE does not support dynamic system properties, no way to re-use
// system properties in samevm/agentvm mode.
/*
* @test
* @bug 8046321 8153829
* @summary OCSP Stapling for TLS
* @library ../../../../java/security/testlibrary
* @build CertificateBuilder SimpleOCSPServer
* @run main/othervm SSLSocketWithStapling
*/
import java.io.*;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.Socket;
import java.net.ServerSocket;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import javax.net.ssl.*;
import java.security.KeyStore;
import java.security.PublicKey;
import java.security.Security;
import java.security.cert.CertPathValidator;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertPathValidatorException.BasicReason;
import java.security.cert.Certificate;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
import java.security.cert.PKIXRevocationChecker;
import java.security.cert.PKIXRevocationChecker.Option;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
public class SSLSocketWithStapling {
/*
* =============================================================
* Set the various variables needed for the tests, then
* specify what tests to run on each side.
*/
// Turn on TLS debugging
static final boolean debug = false;
/*
* Should we run the client or server in a separate thread?
* Both sides can throw exceptions, but do you have a preference
* as to which side should be the main thread.
*/
static boolean separateServerThread = true;
Thread clientThread = null;
Thread serverThread = null;
static String passwd = "serverpass";
/*
* Is the server ready to serve?
*/
volatile static boolean serverReady = false;
volatile int serverPort = 443;
volatile Exception serverException = null;
volatile Exception clientException = null;
// PKI components we will need for this test
static KeyStore serverKeystore; // SSL Server Keystore
static KeyStore trustStore; // SSL Client trust store
// Extra configuration parameters and constants
static final String[] TLS13ONLY = new String[] { "TLSv1.3" };
static final String[] TLS12MAX =
new String[] { "TLSv1.2", "TLSv1.1", "TLSv1" };
/*
* If the client or server is doing some kind of object creation
* that the other side depends on, and that thread prematurely
* exits, you may experience a hang. The test harness will
* terminate all hung threads after its timeout has expired,
* currently 3 minutes by default, but you might try to be
* smart about it....
*/
public static void main(String[] args) throws Exception {
if (debug) {
System.setProperty("javax.net.debug", "ssl:handshake");
}
try {
String keystoreServerPath ="/Users/lexy/Desktop/Clases/Seguridad/almacenes/keystoreServidor.jceks";
String trustClientPath = "/Users/lexy/Desktop/Clases/Seguridad/almacenes/truststoreClient.jceks";
System.setProperty("jdk.security.allowNonCaAnchor", "true" );
System.setProperty("javax.net.ssl.trustStore", trustClientPath);
System.setProperty("javax.net.ssl.trustStoreType", "JCEKS");
System.setProperty("javax.net.ssl.trustStorePassword", "clientpass");
serverKeystore = KeyStore.getInstance("JCEKS");
//System.out.println(passwd_key);
serverKeystore.load(new FileInputStream(keystoreServerPath),"serverpass".toCharArray());
trustStore = KeyStore.getInstance("JCEKS");
//trustedStore.load(new FileInputStream("C:\Users\usuario\Desktop\alamcenes/clientTrustedCerts.jks"), "clientpass".toCharArray());
trustStore.load(new FileInputStream(trustClientPath), "clientpass".toCharArray()); //supuestamente no hay que poner contrase�a es la misma pero no se deberia i dont know
testPKIXParametersRevEnabled(false);
testPKIXParametersRevEnabled(true);
} finally {
}
}
/**
* Do a basic connection using PKIXParameters with revocation checking
* enabled and client-side OCSP disabled. It will only pass if all
* stapled responses are present, valid and have a GOOD status.
*/
static class ClientParameters {
boolean enabled = true;
PKIXBuilderParameters pkixParams = null;
PKIXRevocationChecker revChecker = null;
String[] protocols = null;
String[] ciphers = null;
ClientParameters() { }
}
static class ServerParameters {
boolean enabled = true;
int cacheSize = 256;
int cacheLifetime = 3600;
int respTimeout = 5000;
String respUri = "http://localhost:9999";
boolean respOverride = false;
boolean ignoreExts = false;
String[] protocols = null;
String[] ciphers = null;
ServerParameters() { }
}
static void testPKIXParametersRevEnabled(boolean isTls13) throws Exception {
ClientParameters cliParams = new ClientParameters();
ServerParameters servParams = new ServerParameters();
if (isTls13) {
cliParams.protocols = TLS13ONLY;
servParams.protocols = TLS13ONLY;
} else {
cliParams.protocols = TLS12MAX;
servParams.protocols = TLS12MAX;
}
serverReady = false;
System.out.println("=====================================");
System.out.println("Stapling enabled, PKIXParameters with");
System.out.println("Revocation checking enabled ");
System.out.println("=====================================");
cliParams.pkixParams = new PKIXBuilderParameters(trustStore,
new X509CertSelector());
cliParams.pkixParams.setRevocationEnabled(true);
Security.setProperty("ocsp.enable", "false");
cliParams.enabled=true;
servParams.enabled=true;
servParams.respUri="http://localhost:9999";
SSLSocketWithStapling sslTest = new SSLSocketWithStapling(cliParams,
servParams);
TestResult tr = sslTest.getResult();
if (tr.clientExc != null) {
throw tr.clientExc;
} else if (tr.serverExc != null) {
throw tr.serverExc;
}
System.out.println(" PASS");
System.out.println("=====================================\n");
}
/*
* Define the server side of the test.
*
* If the server prematurely exits, serverReady will be set to true
* to avoid infinite hangs.
*/
void doServerSide(ServerParameters servParams) throws Exception {
// Selectively enable or disable the feature
System.setProperty("jdk.tls.server.enableStatusRequestExtension",
Boolean.toString(servParams.enabled));
// Set all the other operating parameters
System.setProperty("jdk.tls.stapling.cacheSize",
Integer.toString(servParams.cacheSize));
System.setProperty("jdk.tls.stapling.cacheLifetime",
Integer.toString(servParams.cacheLifetime));
System.setProperty("jdk.tls.stapling.responseTimeout",
Integer.toString(servParams.respTimeout));
System.setProperty("jdk.tls.stapling.responderURI", servParams.respUri);
System.setProperty("jdk.tls.stapling.responderOverride",
Boolean.toString(servParams.respOverride));
System.setProperty("jdk.tls.stapling.ignoreExtensions",
Boolean.toString(servParams.ignoreExts));
// Set keystores and trust stores for the server
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(serverKeystore, passwd.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(trustStore);
SSLContext sslc = SSLContext.getInstance("TLS");
sslc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
SSLServerSocketFactory sslssf = new CustomizedServerSocketFactory(sslc,
servParams.protocols, servParams.ciphers);
try (SSLServerSocket sslServerSocket =
(SSLServerSocket) sslssf.createServerSocket(serverPort)) {
serverPort = sslServerSocket.getLocalPort();
/*
* Signal Client, we're ready for his connect.
*/
serverReady = true;
try (SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
InputStream sslIS = sslSocket.getInputStream();
OutputStream sslOS = sslSocket.getOutputStream()) {
int numberIn = sslIS.read();
int numberSent = 85;
log("Server received number: " + numberIn);
sslOS.write(numberSent);
sslOS.flush();
log("Server sent number: " + numberSent);
}
}
}
/*
* Define the client side of the test.
*
* If the server prematurely exits, serverReady will be set to true
* to avoid infinite hangs.
*/
void doClientSide(ClientParameters cliParams) throws Exception {
// Wait 5 seconds for server ready
for (int i = 0; (i < 100 && !serverReady); i++) {
Thread.sleep(50);
}
if (!serverReady) {
throw new RuntimeException("Server not ready yet");
}
// Selectively enable or disable the feature
System.setProperty("jdk.tls.client.enableStatusRequestExtension",
Boolean.toString(cliParams.enabled));
// Create the Trust Manager Factory using the PKIX variant
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
// If we have a customized pkixParameters then use it
if (cliParams.pkixParams != null) {
// LIf we have a customized PKIXRevocationChecker, add
// it to the PKIXBuilderParameters.
if (cliParams.revChecker != null) {
cliParams.pkixParams.addCertPathChecker(cliParams.revChecker);
}
ManagerFactoryParameters trustParams =
new CertPathTrustManagerParameters(cliParams.pkixParams);
tmf.init(trustParams);
} else {
tmf.init(trustStore);
}
SSLContext sslc = SSLContext.getInstance("TLS");
sslc.init(null, tmf.getTrustManagers(), null);
SSLSocketFactory sslsf = new CustomizedSocketFactory(sslc,
cliParams.protocols, cliParams.ciphers);
try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket("localhost",
serverPort);
InputStream sslIS = sslSocket.getInputStream();
OutputStream sslOS = sslSocket.getOutputStream()) {
int numberSent = 80;
sslOS.write(numberSent);
sslOS.flush();
log("Client sent number: " + numberSent);
int numberIn = sslIS.read();
log("Client received number:" + numberIn);
}
}
/*
* Primary constructor, used to drive remainder of the test.
*
* Fork off the other side, then do your work.
*/
SSLSocketWithStapling(ClientParameters cliParams,
ServerParameters servParams) throws Exception {
Exception startException = null;
try {
if (separateServerThread) {
startServer(servParams, true);
startClient(cliParams, false);
} else {
startClient(cliParams, true);
startServer(servParams, false);
}
} catch (Exception e) {
startException = e;
}
/*
* Wait for other side to close down.
*/
if (separateServerThread) {
if (serverThread != null) {
serverThread.join();
}
} else {
if (clientThread != null) {
clientThread.join();
}
}
}
TestResult getResult() {
TestResult tr = new TestResult();
tr.clientExc = clientException;
tr.serverExc = serverException;
return tr;
}
void startServer(ServerParameters servParams, boolean newThread)
throws Exception {
if (newThread) {
serverThread = new Thread() {
public void run() {
try {
doServerSide(servParams);
} catch (Exception e) {
/*
* Our server thread just died.
*
* Release the client, if not active already...
*/
System.err.println("Server died...");
e.printStackTrace(System.err);
serverReady = true;
serverException = e;
}
}
};
serverThread.start();
} else {
try {
doServerSide(servParams);
} catch (Exception e) {
serverException = e;
} finally {
serverReady = true;
}
}
}
void startClient(ClientParameters cliParams, boolean newThread)
throws Exception {
if (newThread) {
clientThread = new Thread() {
public void run() {
try {
doClientSide(cliParams);
} catch (Exception e) {
/*
* Our client thread just died.
*/
System.err.println("Client died...");
clientException = e;
}
}
};
clientThread.start();
} else {
try {
doClientSide(cliParams);
} catch (Exception e) {
clientException = e;
}
}
}
/**
* Log a message on stdout
*
* @param message The message to log
*/
private static void log(String message) {
if (debug) {
System.out.println(message);
}
}
// The following two classes are Simple nested class to group a handful
// of configuration parameters used before starting a client or server.
// We'll just access the data members directly for convenience.
static class CustomizedSocketFactory extends SSLSocketFactory {
final SSLContext sslc;
final String[] protocols;
final String[] cipherSuites;
CustomizedSocketFactory(SSLContext ctx, String[] prots, String[] suites)
throws GeneralSecurityException {
super();
sslc = (ctx != null) ? ctx : SSLContext.getDefault();
protocols = prots;
cipherSuites = suites;
}
@Override
public Socket createSocket(Socket s, String host, int port,
boolean autoClose) throws IOException {
Socket sock = sslc.getSocketFactory().createSocket(s, host, port,
autoClose);
customizeSocket(sock);
return sock;
}
@Override
public Socket createSocket(InetAddress host, int port)
throws IOException {
Socket sock = sslc.getSocketFactory().createSocket(host, port);
customizeSocket(sock);
return sock;
}
@Override
public Socket createSocket(InetAddress host, int port,
InetAddress localAddress, int localPort) throws IOException {
Socket sock = sslc.getSocketFactory().createSocket(host, port,
localAddress, localPort);
customizeSocket(sock);
return sock;
}
@Override
public Socket createSocket(String host, int port)
throws IOException {
Socket sock = sslc.getSocketFactory().createSocket(host, port);
customizeSocket(sock);
return sock;
}
@Override
public Socket createSocket(String host, int port,
InetAddress localAddress, int localPort)
throws IOException {
Socket sock = sslc.getSocketFactory().createSocket(host, port,
localAddress, localPort);
customizeSocket(sock);
return sock;
}
@Override
public String[] getDefaultCipherSuites() {
return sslc.getDefaultSSLParameters().getCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return sslc.getSupportedSSLParameters().getCipherSuites();
}
private void customizeSocket(Socket sock) {
if (sock instanceof SSLSocket) {
if (protocols != null) {
((SSLSocket)sock).setEnabledProtocols(protocols);
}
if (cipherSuites != null) {
((SSLSocket)sock).setEnabledCipherSuites(cipherSuites);
}
}
}
}
static class CustomizedServerSocketFactory extends SSLServerSocketFactory {
final SSLContext sslc;
final String[] protocols;
final String[] cipherSuites;
CustomizedServerSocketFactory(SSLContext ctx, String[] prots, String[] suites)
throws GeneralSecurityException {
super();
sslc = (ctx != null) ? ctx : SSLContext.getDefault();
protocols = prots;
cipherSuites = suites;
}
@Override
public ServerSocket createServerSocket(int port) throws IOException {
ServerSocket sock =
sslc.getServerSocketFactory().createServerSocket(port);
customizeSocket(sock);
return sock;
}
@Override
public ServerSocket createServerSocket(int port, int backlog)
throws IOException {
ServerSocket sock =
sslc.getServerSocketFactory().createServerSocket(port,
backlog);
customizeSocket(sock);
return sock;
}
@Override
public ServerSocket createServerSocket(int port, int backlog,
InetAddress ifAddress) throws IOException {
ServerSocket sock =
sslc.getServerSocketFactory().createServerSocket(port,
backlog, ifAddress);
customizeSocket(sock);
return sock;
}
@Override
public String[] getDefaultCipherSuites() {
return sslc.getDefaultSSLParameters().getCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return sslc.getSupportedSSLParameters().getCipherSuites();
}
private void customizeSocket(ServerSocket sock) {
if (sock instanceof SSLServerSocket) {
if (protocols != null) {
((SSLServerSocket)sock).setEnabledProtocols(protocols);
}
if (cipherSuites != null) {
((SSLServerSocket)sock).setEnabledCipherSuites(cipherSuites);
}
}
}
}
static class TestResult {
Exception serverExc = null;
Exception clientExc = null;
}
}
问题已解决:
在我的例子中,服务器装订不工作导致服务器证书配置错误。
服务器证书必须链接到根 CA 证书,而我的是单独的。另外,我在证书上指定了 authorityInfoAccess 扩展。
所以:
我用于签署 ssl 证书的 openssl 配置文件如下所示(请参阅带有 OCSP URI 的 authorityInfoAccess):
[ auth_cert ]
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = critical, serverAuth,clientAuth
authorityInfoAccess = OCSP;URI:http://localhost:9999
还要确保您的 ssl 证书与 CA 或 CA 和中间证书链接在一起,具体取决于您的设置。
如果是per format,就拼接起来,在linux中用cat工具,我用的命令是:
cat ca.crt.pem >> server/serverauth.crt.pem
有关我如何实现的更多信息,请查看我在此 coderanch thread 上的帖子。