最近在做一个https双向认证的工作,领导先让我实现,我之前写了一篇文章,把tomcat的生成证书和配置的实现写了出来。
现在领导给了我服务器的CA证书的客户端证书和私钥,服务端信任证书,分别是crt和pem格式的文件,
试了很多方法,才把这个搞通,由于不同的平台,证书格式和版本差异很大,包括文本证书和二进制证书文件。
通过openSSL 很容易把证书转换出来,现有Linux主机,安装了openssl,
证书文件:client.crt ,server.crt ,client.pem
openssl pkcs12 -export -in client.crt -out client.pfx -inkey client.pem -passin pass:123456
生成的pfx格式的证书,可以安装在windows系统中,可以通过浏览器访问,
程序中由于双向认证,需要信任服务端证书,
把server.crt转换为keystore格式
keytool -importkeystore -v -srckeystore client.pfx -srcstoretype pkcs12 -srcstorepass 123456 -destkeystore client.keystore -deststoretype jks -deststorepass 123456
keytool -import -alias ca -trustcacerts -file server.crt -keystore client.keystore
把keystore设成客户端信任证书即可实现双向认证。
import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.security.KeyStore; import java.util.ArrayList; import java.util.List; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import java.security.cert.X509Certificate; import junit.framework.TestCase; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.conn.ssl.X509HostnameVerifier; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.protocol.HTTP; import org.junit.Before; import org.junit.Test; @SuppressWarnings("deprecation") public class HttpsClient extends TestCase { private String httpUrl = "https://127.0.0.1:443/TESTl/startupServlet"; // 客户端密钥库 private String sslKeyStorePath; private String sslKeyStorePassword; private String sslKeyStoreType; // 客户端信任的证书 private String sslTrustStore; private String sslTrustStorePassword; @Before public void setUp() { sslKeyStorePath = "client.pfx"; sslKeyStorePassword = "123456"; sslKeyStoreType = "PKCS12"; // 密钥库类型,有JKS PKCS12等 sslTrustStore = "client.keystore"; sslTrustStorePassword = "123456"; System.setProperty("javax.net.ssl.keyStore", sslKeyStorePath); System.setProperty("javax.net.ssl.keyStorePassword", sslKeyStorePassword); System.setProperty("javax.net.ssl.keyStoreType", sslKeyStoreType); // 设置系统参数 System.setProperty("javax.net.ssl.trustStore", sslTrustStore); System.setProperty("javax.net.ssl.trustStorePassword", sslTrustStorePassword); } @Test public void testHttpsClient() { SSLContext sslContext = null; try { KeyStore kstore = KeyStore.getInstance("PKCS12"); kstore.load(new FileInputStream(sslKeyStorePath), sslKeyStorePassword.toCharArray()); KeyManagerFactory keyFactory = KeyManagerFactory .getInstance("sunx509"); keyFactory.init(kstore, sslKeyStorePassword.toCharArray()); KeyStore tstore = KeyStore.getInstance("jks"); tstore.load(new FileInputStream(sslTrustStore), sslTrustStorePassword.toCharArray()); TrustManager[] tm; TrustManagerFactory tmf = TrustManagerFactory .getInstance("sunx509"); tmf.init(tstore); tm = tmf.getTrustManagers(); sslContext = SSLContext.getInstance("SSL"); sslContext.init(keyFactory.getKeyManagers(), tm, null); } catch (Exception e) { e.printStackTrace(); } try { X509HostnameVerifier hostnameVerifier = new X509HostnameVerifier() { public boolean verify(String arg0, SSLSession arg1) { return true; } @Override public void verify(String arg0, SSLSocket arg1) throws IOException {} @Override public void verify(String arg0, String[] arg1, String[] arg2) throws SSLException {} @Override public void verify(String arg0, X509Certificate arg1) throws SSLException {} }; HttpClient httpClient = new DefaultHttpClient(); SSLSocketFactory socketFactory = new SSLSocketFactory(sslContext); socketFactory.setHostnameVerifier(hostnameVerifier); Scheme sch = new Scheme("https", 443, socketFactory); httpClient.getConnectionManager().getSchemeRegistry().register(sch); HttpPost httpPost = new HttpPost(httpUrl); List<NameValuePair> nvps = new ArrayList<NameValuePair>(); // nvps.add(new BasicNameValuePair("pageSize", "1")); // nvps.add(new BasicNameValuePair("name", null)); httpPost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8)); HttpResponse httpResponse = httpClient.execute(httpPost); String spt = System.getProperty("line.separator"); BufferedReader buffer = new BufferedReader(new InputStreamReader( httpResponse.getEntity().getContent())); StringBuffer stb = new StringBuffer(); String line = null; while ((line = buffer.readLine()) != null) { stb.append(line); } buffer.close(); String result = stb.toString(); System.out.println("result=" + result); } catch (Exception e) { e.printStackTrace(); } } }
中间的重写方法是由于证书中的主机名IP与实际访问的不符合,这里是去除commonName的校验。
声明:本文为原创,转载请注明出处。