Java加密和数字签名编程快速入门
副标题#e#
本文主要谈一下暗码学中的加密和数字签名,以及其在java中如何举办利用。对暗码学有乐趣的同伴,推荐看Bruce Schneier的著作:Applied Crypotography。在jdk1.5的刊行版本中安详性方面有了很大的改造,也提供了对RSA算法的直接支持,此刻我们从实例入手办理问题(本文仅是作为简朴先容):
一、暗码学上常用的观念
1)动静摘要:
这是一种与动静认证码团结利用以确保动静完整性的技能。主要利用单向散列函数算法,可用于检讨动静的完整性,和通过散列暗码直接以文本形式生存等,今朝遍及利用的算法有MD4、MD5、SHA-1,jdk1.5对上面都提供了支持,在java中进动作静摘要很简朴, java.security.MessageDigest提供了一个浅易的操纵要领:
/**
*MessageDigestExample.java
*Copyright 2005-2-16
*/
import java.security.MessageDigest;
/**
*单一的动静摘要算法,不利用暗码.可以用来对明文动静(如:暗码)埋没生存
*/
public class MessageDigestExample{
public static void main(String[] args) throws Exception{
if(args.length!=1){
System.err.println("Usage:java MessageDigestExample text");
System.exit(1);
}
byte[] plainText=args[0].getBytes("UTF8");
//利用getInstance("算法")来得到动静摘要,这里利用SHA-1的160位算法
MessageDigest messageDigest=MessageDigest.getInstance("SHA-1");
System.out.println("\n"+messageDigest.getProvider().getInfo());
//开始利用算法
messageDigest.update(plainText);
System.out.println("\nDigest:");
//输出算法运算功效
System.out.println(new String(messageDigest.digest(),"UTF8"));
}
}
还可以通过动静认证码来举办加密实现,javax.crypto.Mac提供了一个办理方案,有乐趣者可以参考相关API文档,本文只是简朴先容什么是摘要算法。
#p#副标题#e#
2)私钥加密:
动静摘要只能查抄动静的完整性,可是单向的,对明文动静并不能加密,要加密明文的动静的话,就要利用其他的算法,要确保机要性,我们需要利用私钥暗码术来互换私有动静。
这种最好领略,利用对称算法。好比:A用一个密钥对一个文件加密,而B读取这个文件的话,则需要和A一样的密钥,两边共享一个私钥(而在web情况下,私钥在通报时容易被侦听):
利用私钥加密的话,首先需要一个密钥,可用javax.crypto.KeyGenerator发生一个密钥(java.security.Key),然后通报给一个加密东西(javax.crypto.Cipher),该东西再利用相应的算法来举办加密,主要对称算法有:DES(实际密钥只用到56位),AES(支持三种密钥长度:128、192、256位),凡是首先128位,其他的尚有DESede等,jdk1.5种也提供了对对称算法的支持,以下例子利用AES算法来加密:
/** *PrivateExmaple.java *Copyright 2005-2-16 */ import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import java.security.Key; /** *私鈅加密,担保动静机要性 */ public class PrivateExample{ public static void main(String[] args) throws Exception{ if(args.length!=1){ System.err.println("Usage:java PrivateExample <text>"); System.exit(1); } byte[] plainText=args[0].getBytes("UTF8"); //通过KeyGenerator形成一个key System.out.println("\nStart generate AES key"); KeyGenerator keyGen=KeyGenerator.getInstance("AES"); keyGen.init(128); Key key=keyGen.generateKey(); System.out.println("Finish generating DES key"); //得到一个私鈅加密类Cipher,ECB是加密方法,PKCS5Padding是填充要领 Cipher cipher=Cipher.getInstance("AES/ECB/PKCS5Padding"); System.out.println("\n"+cipher.getProvider().getInfo()); //利用私鈅加密 System.out.println("\nStart encryption:"); cipher.init(Cipher.ENCRYPT_MODE,key); byte[] cipherText=cipher.doFinal(plainText); System.out.println("Finish encryption:"); System.out.println(new String(cipherText,"UTF8")); System.out.println("\nStart decryption:"); cipher.init(Cipher.DECRYPT_MODE,key); byte[] newPlainText=cipher.doFinal(cipherText); System.out.println("Finish decryption:"); System.out.println(new String(newPlainText,"UTF8")); } }
3)公钥加密:
上面提到,私钥加密需要一个共享的密钥,那么如何通报密钥呢?web情况下,直接通报的话很容易被侦听到,幸好有了公钥加密的呈现。公钥加密也叫差池称加密,差池称算法利用一对密钥对,一个公钥,一个私钥,利用公钥加密的数据,只有私钥能解开(可用于加密);同时,利用私钥加密的数据,只有公钥能解开(签名)。可是速度很慢(比私钥加密慢100到1000倍),公钥的主要算法有RSA,还包罗Blowfish,Diffie-Helman等,jdk1.5种提供了对RSA的支持,是一个改造的处所:
/** *PublicExample.java *Copyright 2005-2-16 */ import java.security.Key; import javax.crypto.Cipher; import java.security.KeyPairGenerator; import java.security.KeyPair; /** *一个简朴的公鈅加密例子,Cipher类利用KeyPairGenerator生成的公鈅和私鈅 */ public class PublicExample{ public static void main(String[] args) throws Exception{ if(args.length!=1){ System.err.println("Usage:java PublicExample <text>"); System.exit(1); } byte[] plainText=args[0].getBytes("UTF8"); //组成一个RSA密钥 System.out.println("\nStart generating RSA key"); KeyPairGenerator keyGen=KeyPairGenerator.getInstance("RSA"); keyGen.initialize(1024); KeyPair key=keyGen.generateKeyPair(); System.out.println("Finish generating RSA key"); //得到一个RSA的Cipher类,利用公鈅加密 Cipher cipher=Cipher.getInstance("RSA/ECB/PKCS1Padding"); System.out.println("\n"+cipher.getProvider().getInfo()); System.out.println("\nStart encryption"); cipher.init(Cipher.ENCRYPT_MODE,key.getPublic()); byte[] cipherText=cipher.doFinal(plainText); System.out.println("Finish encryption:"); System.out.println(new String(cipherText,"UTF8")); //利用私鈅解密 System.out.println("\nStart decryption"); cipher.init(Cipher.DECRYPT_MODE,key.getPrivate()); byte[] newPlainText=cipher.doFinal(cipherText); System.out.println("Finish decryption:"); System.out.println(new String(newPlainText,"UTF8")); } }
4)数字签名:
#p#分页标题#e#
数字签名,它是确定互换动静的通信方身份的第一个级别。上面A通过利用公钥加密数据后发给B,B操作私钥解密就获得了需要的数据,问题来了,由于都是利用公钥加密,那么如何检讨是A发过来的动静呢?上面也提到了一点,私钥是独一的,那么A就可以操作A本身的私钥举办加密,然后B再操作A的公钥来解密,就可以了;数字签名的道理就基于此,而凡是为了证明发送数据的真实性,通过操作动静摘要得到简短的动静内容,然后再操作私钥举办加密散列数据和动静一起发送。java中为数字签名提供了精采的支持,java.security.Signature类提供了动静签名:
/**
*DigitalSignature2Example.java
*Copyright 2005-2-16
*/
import java.security.Signature;
import java.security.KeyPairGenerator;
import java.security.KeyPair;
import java.security.SignatureException;
/**
*数字签名,利用RSA私钥对对动静摘要签名,然后利用公鈅验证 测试
*/
public class DigitalSignature2Example{
public static void main(String[] args) throws Exception{
if(args.length!=1){
System.err.println("Usage:java DigitalSignature2Example <text>");
System.exit(1);
}
byte[] plainText=args[0].getBytes("UTF8");
//形成RSA公钥对
System.out.println("\nStart generating RSA key");
KeyPairGenerator keyGen=KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024);
KeyPair key=keyGen.generateKeyPair();
System.out.println("Finish generating RSA key");
//利用私鈅签名
Signature sig=Signature.getInstance("SHA1WithRSA");
sig.initSign(key.getPrivate());
sig.update(plainText);
byte[] signature=sig.sign();
System.out.println(sig.getProvider().getInfo());
System.out.println("\nSignature:");
System.out.println(new String(signature,"UTF8"));
//利用公鈅验证
System.out.println("\nStart signature verification");
sig.initVerify(key.getPublic());
sig.update(plainText);
try{
if(sig.verify(signature)){
System.out.println("Signature verified");
}else System.out.println("Signature failed");
}catch(SignatureException e){
System.out.println("Signature failed");
}
}
}
5)数字证书。
#p#分页标题#e#
尚有个问题,就是公钥问题,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=你的暗码