java 证书 ssl_Java SSL 证书细节

本文详细介绍了Java中SSL证书的应用,包括证书的生成、格式转换、加密原理以及单向认证的实现。通过示例代码展示了如何在Java中配置SSLContext,使用TrustManager进行服务器证书的信任管理,并从代码层面获取服务器证书。
摘要由CSDN通过智能技术生成

关于SSL这块,网上很多,但很多都是讲原理或怎么生成证书实现简单通信,没有讲到实践时的诸多细节。

SSL, 即Secure Sockets Layer 安全套接层。本文介绍SSL的一些常见问题,用的语言主要是Java。

首先明确SSL的目的,就是加密。就是在Socket的基础上增加安全性。一般来讲,就是避免客户端向服务端传输数据时,被人拦截篡改,避免所谓的中间人攻击,防范钓鱼网站。

关于它的基础概念,可以看

【上】安全HTTPS-全面详解对称加密,非对称加密,数字签名,数字证书和HTTPS

【下】安全HTTPS-全面详解对称加密,非对称加密,数字签名,数字证书和HTTPS

问题1:证书生成和格式

网上搜索SSL,有很多生成证书的教程。可是会发现有的是用Java的Keytool生成的cer,有的是用openssl来生成的pem。这是为什么呢?其实这些证书本质是一样的,只是文件格式不一样。所以如果想在Java里使用pem格式的证书,就要转化。转化的方式在这个问题里已有回答:

Convert a PEM-formatted String to a java.security.cert.X509Certificate

另外,公钥和私钥分别以什么形式存在?公钥存在于证书文件中,私钥存在于Keystore文件中。注意Java里的Keystore类可以是有私钥的Keystore,也可以是只有公钥的Keystore。

还有,为什么使用Java keytool生成的证书要设置密码?这个密码是JKS文件的密码,注意不是公钥或私钥哦,是用于防范别人随便乱拿的。

问题2:既然是加密,那么可不可以脱离Socket存在?

可以。可以直接使用公钥来加密数据,再用私钥解密数据。具体方法参考:

Java加密技术(八)——数字证书

问题3:单向认证,即只确认服务端是否真实可靠的话,要做什么?以SSLSocket举例。

如果只需要信任自己生成的证书

代码:

public class TestSSLSocketClient {

private static String path = "e:\\keytool\\sslclient.keystore";

private static char[] password = "aaaaaaa".toCharArray();

/**

* @param args

*/

public static void main(String[] args) {

SSLContext context = null;

try {

KeyStore ts = KeyStore.getInstance("JKS");

ts.load(new FileInputStream(path), password);

TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");

tmf.init(ts);

TrustManager [] tm = tmf.getTrustManagers();

context = SSLContext.getInstance("SSL");

context.init(null, tm, null);

} catch (...... e) { //省略捕获的异常信息

e.printStackTrace();

}

SSLSocketFactory ssf = context.getSocketFactory();

try {

SSLSocket ss = (SSLSocket) ssf.createSocket("localhost", 8000);

System.out.println("客户端就绪。");

ObjectInputStream br = new ObjectInputStream(ss.getInputStream());

try {

System.out.println(br.readObject());

} catch (ClassNotFoundException e) {

e.printStackTrace();

}

br.close();

ss.close();

System.out.println("客户端测试ok");

} catch (UnknownHostException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

}

}

可以从代码看到,对于SSLContext,在做单向认证时,只需要TrustManagerFactory即可,

context.init(null, tm, null);

TrustManagerFactory是使用了服务端的证书的,即只使用了公钥来加密数据。

如果只需要信任系统自带的证书

SSLContext sslContext = SSLContext.getDefault();

TrustManagerFactory tmf = TrustManagerFactory

.getInstance(TrustManagerFactory.getDefaultAlgorithm());

// Using null here initialises the TMF with the default trust store.

tmf.init((KeyStore) null);

// Get hold of the default trust manager

X509TrustManager defaultTm = null;

for (TrustManager tm : tmf.getTrustManagers()) {

if (tm instanceof X509TrustManager) {

defaultTm = (X509TrustManager) tm;

break;

}

}

FileInputStream myKeys = new FileInputStream("truststore.jks");

// Do the same with your trust store this time

// Adapt how you load the keystore to your needs

KeyStore myTrustStore = KeyStore.getInstance(KeyStore.getDefaultType());

myTrustStore.load(myKeys, "password".toCharArray());

myKeys.close();

tmf = TrustManagerFactory

.getInstance(TrustManagerFactory.getDefaultAlgorithm());

tmf.init(myTrustStore);

// Get hold of the default trust manager

X509TrustManager myTm = null;

for (TrustManager tm : tmf.getTrustManagers()) {

if (tm instanceof X509TrustManager) {

myTm = (X509TrustManager) tm;

break;

}

}

// Wrap it in your own class.

final X509TrustManager finalDefaultTm = defaultTm;

final X509TrustManager finalMyTm = myTm;

X509TrustManager customTm = new X509TrustManager() {

@Override

public X509Certificate[] getAcceptedIssuers() {

// If you're planning to use client-cert auth,

// merge results from "defaultTm" and "myTm".

return finalDefaultTm.getAcceptedIssuers();

}

@Override

public void checkServerTrusted(X509Certificate[] chain,

String authType) throws CertificateException {

try {

finalMyTm.checkServerTrusted(chain, authType);

} catch (CertificateException e) {

// This will throw another CertificateException if this fails too.

finalDefaultTm.checkServerTrusted(chain, authType);

}

}

@Override

public void checkClientTrusted(X509Certificate[] chain,

String authType) throws CertificateException {

// If you're planning to use client-cert auth,

// do the same as checking the server.

finalDefaultTm.checkClientTrusted(chain, authType);

}

};

SSLContext sslContext = SSLContext.getInstance("TLS");

sslContext.init(null, new TrustManager[] { customTm }, null);

问题4:可以看到SSLContext.init()的参数有KeyManager和TrustManager,它俩的差异是?

问题5:服务器端会在连接时向客户端发送证书,那么如何从代码上获取?

import java.io.InputStream;

import java.io.OutputStream;

import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;

import javax.net.ssl.SSLSocket;

import javax.net.ssl.SSLSession;

import javax.net.ssl.SSLSocketFactory;

import javax.net.ssl.TrustManager;

import javax.net.ssl.X509TrustManager;

public class RetrieveSSLCert {

public static void main(String[] args) throws Exception {

if (args.length < 2) {

System.out.println("Usage: java RetrieveSSLCert ");

return;

}

String host = args[0];

int port = Integer.parseInt(args[1]);

// create custom trust manager to ignore trust paths

TrustManager trm = new X509TrustManager() {

public X509Certificate[] getAcceptedIssuers() {

return null;

}

public void checkClientTrusted(X509Certificate[] certs, String authType) {

}

public void checkServerTrusted(X509Certificate[] certs, String authType) {

}

};

SSLContext sc = SSLContext.getInstance("SSL");

sc.init(null, new TrustManager[] { trm }, null);

SSLSocketFactory factory =sc.getSocketFactory();

SSLSocket socket =(SSLSocket)factory.createSocket(host, port);

socket.startHandshake();

SSLSession session = socket.getSession();

java.security.cert.Certificate[] servercerts = session.getPeerCertificates();

for (int i = 0; i < servercerts.length; i++) {

System.out.print("-----BEGIN CERTIFICATE-----\n");

System.out.print(new sun.misc.BASE64Encoder().encode(servercerts[i].getEncoded()));

System.out.print("\n-----END CERTIFICATE-----\n");

}

socket.close();

}

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值