我已合并client-certificate-with-com-sun-net-httpserver-httpsserver 使用simple-java-https-server,但我总是收到错误消息
SSL-Peer could not be verified.
我调用setWantClientAuth(true)并通过调用
验证AuthentificationCertificate[] peerCerts = pHttpsExchange.getSSLSession().getPeerCertificates();
服务器使用JDK 1.8运行,客户端在Android上运行。服务器代码是:
package de.org.vnetz;
import java.io.*;
import java.net.InetSocketAddress;
import java.security.KeyStore;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import com.sun.net.httpserver.*;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.naming.InvalidNameException;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
import javax.net.ssl.SSLContext;
import javax.security.auth.x500.X500Principal;
import java.security.cert.Certificate;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class clsHTTPSServer {
final static String SERVER_PWD = "xxxxxx";
final static String KST_SERVER = "server.jks";
final static String TST_SERVER = "servertrust.jks";
private static final int PORT = 9999;
public static class MyHandler implements HttpHandler {
// whether to use client cert authentication
private final boolean useClientCertAuth = true;
private List<LdapName> allowedPrincipals = new ArrayList<LdapName>();
private final boolean extendedClientCheck = true;
private static final String CLIENTAUTH_OID = "1.3.6.1.5.5.7.3.2";
@Override
public void handle(HttpExchange t) throws IOException {
String response = "Hallo Natalie!";
HttpsExchange httpsExchange = (HttpsExchange) t;
boolean auth;
try
{
checkAuthentication(httpsExchange);
auth = true;
}
catch (Exception ex)
{
response = ex.getMessage();
auth = false;
}
boolean res = httpsExchange.getSSLSession().isValid();
if (res) {
String qry = httpsExchange.getRequestURI().getQuery();
if (qry!=null && qry.startsWith("qry=")) {
httpsExchange.getResponseHeaders().add("Access-Control-Allow-Origin", "*");
httpsExchange.sendResponseHeaders(200, response.length());
OutputStream os = t.getResponseBody();
os.write(response.getBytes());
os.close();
}
else
{
httpsExchange.getResponseHeaders().add("Access-Control-Allow-Origin", "*");
httpsExchange.sendResponseHeaders(200, response.length());
OutputStream os = t.getResponseBody();
os.write((response + " no query!").getBytes());
os.close();
}
}
}
// Verify https certs if its Https request and we have SSL auth enabled. Will be called before
// handling the request
protected void checkAuthentication(HttpExchange pHttpExchange) throws SecurityException {
// Cast will always work since this handler is only used for Http
HttpsExchange httpsExchange = (HttpsExchange) pHttpExchange;
if (useClientCertAuth) {
checkCertForClientUsage(httpsExchange);
checkCertForAllowedPrincipals(httpsExchange);
}
}
// Check the cert's principal against the list of given allowedPrincipals.
// If no allowedPrincipals are given than every principal is allowed.
// If an empty list as allowedPrincipals is given, no one is allowed to access
private void checkCertForClientUsage(HttpsExchange pHttpsExchange) {
try {
String host = pHttpsExchange.getSSLSession().getPeerHost();
//Principal p = pHttpsExchange.getSSLSession().getPeerPrincipal();
String pr = pHttpsExchange.getSSLSession().getProtocol();
Certificate[] peerCerts = pHttpsExchange.getSSLSession().getPeerCertificates();
if (peerCerts != null && peerCerts.length > 0) {
X509Certificate clientCert = (X509Certificate) peerCerts[0];
// We required that the extended key usage must be present if we are using
// client cert authentication
if (extendedClientCheck &&
(clientCert.getExtendedKeyUsage() == null || !clientCert.getExtendedKeyUsage().contains(CLIENTAUTH_OID))) {
throw new SecurityException("No extended key usage available");
}
}
} catch (ClassCastException e) {
throw new SecurityException("No X509 client certificate");
} catch (CertificateParsingException e) {
throw new SecurityException("Can't parse client cert");
} catch (SSLPeerUnverifiedException e) {
throw new SecurityException("SSL Peer couldn't be verified");
}
}
private void checkCertForAllowedPrincipals(HttpsExchange pHttpsExchange) {
if (allowedPrincipals != null) {
X500Principal certPrincipal;
try {
certPrincipal = (X500Principal) pHttpsExchange.getSSLSession().getPeerPrincipal();
Set<Rdn> certPrincipalRdns = getPrincipalRdns(certPrincipal);
for (LdapName principal : allowedPrincipals) {
for (Rdn rdn : principal.getRdns()) {
if (!certPrincipalRdns.contains(rdn)) {
throw new SecurityException("Principal " + certPrincipal + " not allowed");
}
}
}
} catch (SSLPeerUnverifiedException e) {
throw new SecurityException("SSLPeer unverified");
} catch (ClassCastException e) {
throw new SecurityException("Internal: Invalid Principal class provided " + e);
}
}
}
private Set<Rdn> getPrincipalRdns(X500Principal principal) {
try {
LdapName certAsLdapName =new LdapName(principal.getName());
return new HashSet<Rdn>(certAsLdapName.getRdns());
} catch (InvalidNameException e) {
throw new SecurityException("Cannot parse '" + principal + "' as LDAP name");
}
}
}
/**
* @param args
*/
public static void main(String[] args) throws Exception {
try {
// setup the socket address
InetSocketAddress address = new InetSocketAddress(PORT);
// initialise the HTTPS server
HttpsServer httpsServer = HttpsServer.create(address, 0);
SSLContext sslContext = SSLContext.getInstance("TLS");
// initialise the keystore
// char[] password = "password".toCharArray();
KeyStore ks = KeyStore.getInstance("JKS");
FileInputStream fis = new FileInputStream(KST_SERVER);// ("testkey.jks");
ks.load(fis, SERVER_PWD.toCharArray());// password);
// setup the key manager factory
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, SERVER_PWD.toCharArray());
// setup the trust manager factory
// TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
// tmf.init(ks);
KeyStore ts = KeyStore.getInstance("JKS");
ts.load(new FileInputStream(TST_SERVER), SERVER_PWD.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ts);
// setup the HTTPS context and parameters
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
SSLParameters sslp = sslContext.getSupportedSSLParameters();
//sslp.setNeedClientAuth(true);
sslp.setWantClientAuth(true);
httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext) {
public void configure(HttpsParameters params) {
try {
// initialise the SSL context
SSLContext c = SSLContext.getDefault();
SSLEngine engine = c.createSSLEngine();
//params.setNeedClientAuth(true);
params.setWantClientAuth(true);
params.setCipherSuites(engine.getEnabledCipherSuites());
params.setProtocols(engine.getEnabledProtocols());
// get the default parameters
SSLParameters defaultSSLParameters = c.getDefaultSSLParameters();
SSLParameters sslParams = sslContext.getDefaultSSLParameters();
//sslParams.setNeedClientAuth(true);
sslParams.setWantClientAuth(true);
params.setSSLParameters(defaultSSLParameters);
} catch (Exception ex) {
System.out.println("Failed to create HTTPS port");
}
}
});
httpsServer.createContext("/test", new MyHandler());
httpsServer.setExecutor(
new ThreadPoolExecutor(4, 80, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1000))); // creates
// a
// default
// executor
httpsServer.start();
} catch (Exception exception) {
System.out.println("Failed to create HTTPS server on port " + 62112 + " of localhost");
exception.printStackTrace();
}
}
}
客户端代码是:
package vnetz.de.org.vnetz;
import android.content.Context;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.net.SocketException;
import java.net.URL;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
public class clsHTTPS {
private static final String MYURL = "https://localhost:9999/test?qry=test";
static String NO_KEYSTORE = "";
static String UNAUTH_KEYSTORE = "unauthclient.bks"; // Doesn't exist in server trust store, should fail authentication.
static String AUTH_KEYSTORE = "authclient.bks"; // Exists in server trust store, should pass authentication.
static String TRUSTSTORE = "clienttrust.bks";
static String CLIENT_PWD = "xxxxxx";
private static Context context = null;
public clsHTTPS(Context context) {
this.context = context;
}
public static void main(String[] args) throws Exception {
}
public String connect(String jksFile) {
try {
String https_url = MYURL;
URL url;
url = new URL(https_url);
HttpsURLConnection.setDefaultHostnameVerifier(new NullHostNameVerifier());
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(getSSLFactory(jksFile));
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setUseCaches(false);
// Print response
//SSLContext context = SSLContext.getInstance("TLS");
//context.init(null, new X509TrustManager[]{new NullX509TrustManager()}, new SecureRandom());
//HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
BufferedReader bir = new BufferedReader(new InputStreamReader(conn.getInputStream()));
StringBuilder sbline = new StringBuilder();
String line;
while ((line = bir.readLine()) != null) {
System.out.println(line);
sbline.append(line);
}
bir.close();
conn.disconnect();
return sbline.toString();
} catch (SSLHandshakeException | SocketException e) {
System.out.println(e.getMessage());
System.out.println("");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static SSLSocketFactory getSSLFactory(String jksFile) throws Exception {
// Create key store
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
KeyManager[] kmfs = null;
if (jksFile.length() > 0) {
keyStore.load(context.getAssets().open(jksFile), CLIENT_PWD.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(
KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, CLIENT_PWD.toCharArray());
kmfs = kmf.getKeyManagers();
}
// create trust store (validates the self-signed server!)
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(context.getAssets().open(TRUSTSTORE), CLIENT_PWD.toCharArray());
TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustFactory.init(trustStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmfs, trustFactory.getTrustManagers(), null);
return sslContext.getSocketFactory();
}
private class NullHostNameVerifier implements HostnameVerifier
{
@Override
public boolean verify(String s, SSLSession sslSession)
{
return s.equalsIgnoreCase("localhost");
}
}
private class NullX509TrustManager implements X509TrustManager
{
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException
{
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException
{
}
@Override
public X509Certificate[] getAcceptedIssuers()
{
return new X509Certificate[0];
}
}
}
答案 0 :(得分:1)
服务器中的“未验证对等体”意味着客户端未发送证书,这可能意味着其签名者不在服务器的信任库中。当服务器请求客户端证书时,它会提供可接受的签名者列表,并且客户端不得发送未经其中一个签名的证书。
否则服务器根本不会要求客户端证书。在这种情况下不适用。
在你的情况下,使用needClientAuth
要简单得多,因为握手会失败而你不必像getPeerCertificates()
那样远。
NB:
SSLSession
有效,否则您将无法建立SSL连接。它变为无效的唯一方法是调用invalidate()
,这会导致下一个I / O完全重新握手。你正在测试错误的东西。