如何解决如何在 java TLS 服务器上启用 OCSP 装订?
这篇文章是交叉发布的,所以一定要检查更新in coderanch。
我在我的客户端/服务器应用程序上实施 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正确配置的端口显示消息为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
正如所见,客户端 hello 包含状态请求,但是服务器(如果客户端这样做,则应该包含一个)没有编写扩展,就像它没有意识到 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",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,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,InetAddress localAddress,int localPort) throws IOException {
Socket sock = sslc.getSocketFactory().createSocket(host,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 localPort)
throws IOException {
Socket sock = sslc.getSocketFactory().createSocket(host,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[] 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;
}
}
解决方法
问题已解决:
就我而言,服务器 Stapling 无法正常工作导致服务器证书配置错误。
服务器证书必须链接到根 CA 证书,而我的是单独的。此外,我在证书上指定了 authorityInfoAccess 扩展。
所以:
我用于签署 ssl 证书的 openssl 配置文件如下所示(请参阅 authorityInfoAccess 和您的 OCSP 的 URI):
[ 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 上的帖子。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。