数字证书
数字证书。
还有个问题,就是公钥问题,A用私钥加密了,那么B接受到消息后,用A提供的公钥解密;那么现在有个讨厌的C,他把消息拦截了,然后用自己的私钥加密,同时把自己的公钥发给B,并告诉B,那是A的公钥,结果……,这时候就需要一个中间机构出来说话了(相信权威,我是正确的),就出现了Certificate Authority(也即CA),有名的CA机构有Verisign等,目前数字认证的工业标准是:CCITT的X.509:
数字证书:它将一个身份标识连同公钥一起进行封装,并由称为认证中心或 CA 的第三方进行数字签名。
**库:java平台为你提供了**库,用作**和证书的资源库。从物理上讲,**库是缺省名称为 .keystore 的文件(有一个选项使它成为加密文件)。**和证书可以拥有名称(称为别名),每个别名都由唯一的密码保护。**库本身也受密码保护;您可以选择让每个别名密码与主**库密码匹配。
使用工具keytool,我们来做一件自我认证的事情吧(相信我的认证):
1、创建**库keytool -genkey -v -alias feiUserKey -keyalg RSA 默认在自己的home目录下(windows系统是c:documents and settings<你的用户名> 目录下的。keystore文件),创建我们用 RSA 算法生成别名为 feiUserKey 的自签名的证书,如果使用了-keystore mm 就在当前目录下创建一个**库mm文件来保存**和证书。
2、查看证书:keytool -list 列举了**库的所有的证书
也可以在dos下输入keytool -help查看帮助。
二、JAR的签名
我们已经学会了怎样创建自己的证书了,现在可以开始了解怎样对JAR文件签名,JAR文件在Java中相当于 ZIP 文件,允许将多个 Java 类文件打包到一个具有 .jar 扩展名的文件中,然后可以对这个jar文件进行数字签名,以证实其来源和真实性。该 JAR 文件的接收方可以根据发送方的签名决定是否信任该代码,并可以确信该内容在接收之前没有被篡改过。同时在部署中,可以通过在策略文件中放置访问控制语句根据签名者的身份分配对机器资源的访问权。这样,有些Applet的安全检验访问就得以进行。
使用jarsigner工具可以对jar文件进行签名:
现在假设我们有个Test.jar文件(可以使用jar命令行工具生成):
jarsigner Test.jar feiUserKey (这里我们上面创建了该别名的证书) ,详细信息可以输入jarsigner查看帮助
验证其真实性:jarsigner -verify Test.jar(注意,验证的是jar是否被修改了,但不检验减少的,如果增加了新的内容,也提示,但减少的不会提示。)
使用Applet中:<applet code="Test.class" archive="Test.jar" width="150" height="100"></applet>然后浏览器就会提示你:准许这个会话-拒绝-始终准许-查看证书等。
三、安全套接字层(SSL Secure Sockets Layer)和传输层安全性(TLS Transport Layer Security)
安全套接字层和传输层安全性是用于在客户机和服务器之间构建安全的通信通道的协议。它也用来为客户机认证服务器,以及(不太常用的)为服务器认证客户机。该协议在浏览器应用程序中比较常见,浏览器窗口底部的锁表明 SSL/TLS 有效:
1)当使用 SSL/TLS(通常使用 https:// URL)向站点进行请求时,从服务器向客户机发送一个证书。客户机使用已安装的公共 CA 证书通过这个证书验证服务器的身份,然后检查 IP 名称(机器名)与客户机连接的机器是否匹配。
2)客户机生成一些可以用来生成对话的私钥(称为会话**)的随机信息,然后用服务器的公钥对它加密并将它发送到服务器。服务器用自己的私钥解密消息,然后用该随机信息派生出和客户机一样的私有会话**。通常在这个阶段使用 RSA 公钥算法。
3)客户机和服务器使用私有会话**和私钥算法(通常是 RC4)进行通信。使用另一个**的消息认证码来确保消息的完整性。
java中javax.net.ssl.SSLServerSocketFactory类提供了一个很好的SSLServerSocker的工厂类,熟悉Socket编程的读者可以去练习。当编写完服务器端之后,在浏览器上输入https://主机名:端口 就会通过SSL/TLS进行通话了。注意:运行服务端的时候要带系统环境变量运行:javax.net.ssl.keyStore=**库(创建证书时,名字应该为主机名,比如localhost)和javax.net.ssl.keyStorePassword=你的密码
package com.ijo.security;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.security.KeyStore;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.util.Date;
public class DigitalCertificate {
public static void main(String[] args) {
try {
// 前提:将证书库中的一条证书导出到证书文件(我写的例子里证书文件叫TC.cer)
// 从证书文件TC.cer里读取证书信息
/*
* CertificateFactory cf = CertificateFactory.getInstance("X.509");
* FileInputStream in = new FileInputStream("C:/TC.cer");
* //将文件以文件流的形式读入证书类Certificate中 Certificate c =
* cf.generateCertificate(in);
* System.err.println("转换成String后的证书信息:"+c.toString());
*/
// 或者不用上面代码的方法,直接从证书库中读取证书信息,和上面的结果一摸一样
String pass = "keystore";
FileInputStream in2 = new FileInputStream("C:/BocsoftKeyLib");
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(in2, pass.toCharArray());
String alias = "TestCertification"; // alias为条目的别名
Certificate c = ks.getCertificate(alias);
System.err.println("转换成String后的证书信息:" + c.toString());
// 获取获取X509Certificate类型的对象,这是证书类获取Certificate的子类,实现了更多方法
X509Certificate t = (X509Certificate) c;
// 从信息中提取需要信息
System.out.println("版本号:" + t.getVersion());
System.out.println("***:" + t.getSerialNumber().toString(16));
System.out.println("主体名:" + t.getSubjectDN());
System.out.println("签发者:" + t.getIssuerDN());
System.out.println("有效期:" + t.getNotBefore());
System.out.println("签名算法:" + t.getSigAlgName());
byte[] sig = t.getSignature();// 签名值
PublicKey pk = t.getPublicKey();
byte[] pkenc = pk.getEncoded();
System.out.println("公钥:");
for (int i = 0; i < pkenc.length; i++) {
System.out.print(pkenc[i] + ",");
}
System.err.println();
// 证书的日期有效性检查,颁发的证书都有一个有效性的日期区间
Date TimeNow = new Date();
t.checkValidity(TimeNow);
System.out.println("证书的日期有效性检查:有效的证书日期!");
// 验证证书签名的有效性,通过数字证书认证中心(CA)机构颁布给客户的CA证书,比如:caroot.crt文件
// 我手里没有CA颁给我的证书,所以下面代码执行不了
/*
* FileInputStream in3=new FileInputStream("caroot.crt"); //获取CA证书
* Certificate cac = cf.generateCertificate(in3); //获取CA的公钥
* PublicKey pbk=cac.getPublicKey();
* //c为本地证书,也就是待检验的证书,用CA的公钥校验数字证书c的有效性 c.verify(pbk);
*/
} catch (CertificateExpiredException e) {// 证书的日期有效性检查:过期
System.out.println("证书的日期有效性检查:过期");
} catch (CertificateNotYetValidException e) { // 证书的日期有效性检查:尚未生效
System.out.println("证书的日期有效性检查:尚未生效");
} catch (CertificateException ce) {
ce.printStackTrace();
} catch (FileNotFoundException fe) {
fe.printStackTrace();
} /*
* catch (IOException ioe){ } catch (KeyStoreException kse){ }
*/catch (Exception e) {
e.printStackTrace();
}
}
}