( MD5/SHA1 , DSA , DESede/DES , Diffie-Hellman 的使用)
单钥密码体制是一种传统的加密算法,是指信息的发送方和接收方共同使用同一把密钥进行加解密。
通常 , 使用的加密算法 比较简便高效 , 密钥简短,加解密速度快,破译极其困难。但是加密的安全性依靠密钥保管的安全性 , 在公开的计算机网络上安全地传送和保管密钥是一个严峻的问题,并且如果在多用户的情况下密钥的保管安全性也是一个问题。
单钥密码体制的代表是美国的 DES
一个消息摘要就是一个数据块的数字指纹。即对一个任意长度的一个数据块进行计算,产生一个唯一指印(对于 SHA1 是产生一个 20 字节的二进制数组)。
消息摘要有两个基本属性:
<!---->n <!---->两个不同的报文难以生成相同的摘要
<!---->n <!---->难以对指定的摘要生成一个报文,而由该报文反推算出该指定的摘要
代表:美国国家标准技术研究所的 SHA1 和麻省理工学院 Ronald Rivest 提出的 MD5
密钥一致协议是由 公开密钥密码体制的奠基人 Diffie 和 Hellman 所提出的一种思想。
先决条件 , 允许两名用户在公开媒体上交换信息以生成 " 一致 " 的 , 可以共享的密钥
代表:指数密钥一致协议 (Exponential Key Agreement Protocol),
1976 年, Dittie 和 Hellman 为 解决密钥管理问题,在他们的奠基性的工作“密码学的新方向”一文中,提出一种密钥交换协议,允许在不安全的媒体上通过通讯双方交换信息,安全地传送秘密密 钥。在此新思想的基础上,很快出现了非对称密钥密码体制,即公钥密码体制。在公钥体制中,加密密钥不同于解密密钥,加密密钥公之于众,谁都可以使用;解密 密钥只有解密人自己知道。它们分别称为公开密钥( Public key )和秘密密钥( Private key )。
迄今为止的所有公钥密码体系中, RSA 系统是最著名、最多使用的一种。 RSA 公开密钥密码系统是由 R.Rivest 、 A.Shamir 和 L.Adleman 俊教授于 1977 年提出的。 RSA 的取名就是来自于这三位发明者的姓的第一个字母
所谓数字签名就是信息发送者用其私钥对从所传报文中提取出的特征数据(或称数字指纹)进行 RSA 算法操作,以保证发信人无法抵赖曾发过该信息(即不可抵赖性),同时也确保信息报文在经签名后末被篡改(即完整性)。当信息接收者收到报文后,就可以用发送者的公钥对数字签名进行验证。
在数字签名中有重要作用的数字指纹是通过一类特殊的散列函数( HASH 函数)生成的,对这些 HASH 函数的特殊要求是:
<!---->n <!---->接受的输入报文数据没有长度限制;
<!---->n <!---->对任何输入报文数据生成固定长度的摘要(数字指纹)输出
<!---->n <!---->从报文能方便地算出摘要;
<!---->n <!---->难以对指定的摘要生成一个报文,而由该报文反推算出该指定的摘要;
<!---->n <!---->两个不同的报文难以生成相同的摘要
代表: DSA
Diffie-Hellman 密钥一致协议和 DES 程序需要 JCE 工具库的支持 , 可以到
http://java.sun.com/security/index.html
下载 JCE, 并进行安装
简易安装把 jce1.2.1\lib 下的所有内容复制到 %java_home%\lib\ext 下 ,
如果没有 ext 目录自行建立 , 再把 jce1_2_1.jar 和 sunjce_provider.jar 添加
到 CLASSPATH 内 , 更详细说明请看相应用户手册
使用方法 :
首先用生成一个 MessageDigest 类 , 确定计算方法
java.security.MessageDigest alga=java.security.MessageDigest.getInstance("SHA-1");
添加要进行计算摘要的信息
alga.update(myinfo.getBytes());
计算出摘要
byte[] digesta=alga.digest();
发送给其他人你的信息和摘要
其他人用相同的方法初始化 , 添加信息 , 最后进行比较摘要是否相同
algb.isEqual(digesta,algb.digest())
相关 AIP
java.security.MessageDigest 类
static getInstance(String algorithm)
返回一个 MessageDigest 对象 , 它实现指定的算法
参数 : 算法名 , 如 SHA-1 或 MD5
<!----> <!---->
void update (byte input)
void update (byte[] input)
void update(byte[] input, int offset, int len)
添加要进行计算摘要的信息
<!----> <!---->
byte[] digest()
完成计算 , 返回计算得到的摘要 ( 对于 MD5 是 16 位 ,SHA 是 20 位 )
<!----> <!---->
void reset()
复位
<!----> <!---->
static boolean isEqual(byte[] digesta, byte[] digestb)
比效两个摘要是否相同
<!----> <!---->
代码:
<!----> <!---->
import java.security.*;
public class myDigest {
public static void main(String[] args) {
<!----> <!---->
myDigest my=new myDigest();
my.testDigest();
<!----> <!---->
}
public void testDigest()
{
try {
String myinfo=" 我的测试信息 ";
<!----> <!---->
//java.security.MessageDigest alg=java.security.MessageDigest.getInstance("MD5");
java.security.MessageDigest alga=java.security.MessageDigest.getInstance("SHA-1");
alga.update(myinfo.getBytes());
byte[] digesta=alga.digest();
System.out.println(" 本信息摘要是 :"+byte2hex(digesta));
// 通过某中方式传给其他人你的信息 (myinfo) 和摘要 (digesta) 对方可以判断是否更改或传输正常
java.security.MessageDigest algb=java.security.MessageDigest.getInstance("SHA-1");
algb.update(myinfo.getBytes());
if (algb.isEqual(digesta,algb.digest())) {
System.out.println(" 信息检查正常 ");
}
else
{
System.out.println(" 摘要不相同 ");
}
<!----> <!---->
}
catch (java.security.NoSuchAlgorithmException ex) {
System.out.println(" 非法摘要算法 ");
}
<!----> <!---->
}
public String byte2hex(byte[] b) // 二行制转字符串
{
String hs="";
String stmp="";
for (int n=0;n<b.length;n++)
{
stmp=(java.lang.Integer.toHexString(b[n] & 0XFF));
if (stmp.length()==1) hs=hs+"0"+stmp;
else hs=hs+stmp;
if (n<b.length-1) hs=hs+":";
}
return hs.toUpperCase();
}
<!----> <!---->
}
1 对于一个用户来讲首先要生成他的密钥对 , 并且分别保存
生成一个 KeyPairGenerator 实例
java.security.KeyPairGenerator keygen=java.security.KeyPairGenerator.getInstance("DSA");
如果设定随机产生器就用如相代码初始化
SecureRandom secrand=new SecureRandom();
secrand.setSeed("tttt".getBytes()); // 初始化随机产生器
keygen.initialize(512,secrand); // 初始化密钥生成器
否则
keygen.initialize(512);
生成密钥公钥 pubkey 和私钥 prikey
KeyPair keys=keygen.generateKeyPair(); // 生成密钥组
PublicKey pubkey=keys.getPublic();
PrivateKey prikey=keys.getPrivate();
分别保存在 myprikey.dat 和 mypubkey.dat 中 , 以便下次不在生成
( 生成密钥对的时间比较长
java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("myprikey.dat"));
out.writeObject(prikey);
out.close();
out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("mypubkey.dat"));
out.writeObject(pubkey);
out.close();
<!----> <!---->
<!----> <!---->
2 用他私人密钥 (prikey) 对他所确认的信息 (info) 进行数字签名产生一个签名数组
从文件中读入私人密钥 (prikey)
java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream("myprikey.dat"));
PrivateKey myprikey=(PrivateKey)in.readObject();
in.close();
初始一个 Signature 对象 , 并用私钥对信息签名
java.security.Signature signet=java.security.Signature.getInstance("DSA");
signet.initSign(myprikey);
signet.update(myinfo.getBytes());
byte[] signed=signet.sign();
把信息和签名保存在一个文件中 (myinfo.dat)
java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("myinfo.dat"));
out.writeObject(myinfo);
out.writeObject(signed);
out.close();
把他的公钥的信息及签名发给其它用户
<!----> <!---->
3 其他用户用他的公共密钥 (pubkey) 和签名 (signed) 和信息 (info) 进行验证是否由他签名的信息
读入公钥
java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream("mypubkey.dat"));
PublicKey pubkey=(PublicKey)in.readObject();
in.close();
读入签名和信息
in=new java.io.ObjectInputStream(new java.io.FileInputStream("myinfo.dat"));
String info=(String)in.readObject();
byte[] signed=(byte[])in.readObject();
in.close();
初始一个 Signature 对象 , 并用公钥和签名进行验证
java.security.Signature signetcheck=java.security.Signature.getInstance("DSA");
signetcheck.initVerify(pubkey);
signetcheck.update(info.getBytes());
if (signetcheck.verify(signed)) { System.out.println(" 签名正常 ");}
<!----> <!---->
对于密钥的保存本文是用对象流的方式保存和传送的
也可可以用编码的方式保存
注意要
import java.security.spec.*
import java.security.*
具休说明如下
public key 是用 X.509 编码的 , 例码如下 :
byte[] bobEncodedPubKey=mypublic.getEncoded(); // 生成编码
// 传送二进制编码
// 以下代码转换编码为相应 key 对象
X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec(bobEncodedPubKey);
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
PublicKey bobPubKey = keyFactory.generatePublic(bobPubKeySpec);
<!----> <!---->
对于 Private key 是用 PKCS#8 编码 , 例码如下 :
byte[] bPKCS=myprikey.getEncoded();
// 传送二进制编码
// 以下代码转换编码为相应 key 对象
PKCS8EncodedKeySpec priPKCS8=new PKCS8EncodedKeySpec(bPKCS);
KeyFactory keyf=KeyFactory.getInstance("DSA");
PrivateKey otherprikey=keyf.generatePrivate(priPKCS8);
<!----> <!---->
<!----> <!---->
常用 API
java.security.KeyPairGenerator 密钥生成器类
public static KeyPairGenerator getInstance(String algorithm)
throws NoSuchAlgorithmException
以指定的算法返回一个 KeyPairGenerator 对象
参数 : algorithm 算法名 . 如 :"DSA","RSA"
<!----> <!---->
public void initialize(int keysize)
以指定的长度初始化 KeyPairGenerator 对象 , 如果没有初始化系统以 1024 长度默认设置
参数 :keysize 算法位长 . 其范围必须在 512 到 1024 之间,且必须为 64 的倍数
<!----> <!---->
public void initialize(int keysize, SecureRandom random)
以指定的长度初始化和随机发生器初始化 KeyPairGenerator 对象
参数 :keysize 算法位长 . 其范围必须在 512 到 1024 之间,且必须为 64 的倍数
random 一个随机位的来源 ( 对于 initialize(int keysize) 使用了默认随机器
<!----> <!---->
public abstract KeyPair generateKeyPair()
产生新密钥对
<!----> <!---->
java.security.KeyPair 密钥对类
public PrivateKey getPrivate()
返回私钥
<!----> <!---->
public PublicKey getPublic()
返回公钥
<!----> <!---->
java.security.Signature 签名类
public static Signature getInstance(String algorithm)
throws NoSuchAlgorithmException
返回一个指定算法的 Signature 对象
参数 algorithm 如 :"DSA"
<!----> <!---->
public final void initSign(PrivateKey privateKey)
throws InvalidKeyException
用指定的私钥初始化
参数 :privateKey 所进行签名时用的私钥
<!----> <!---->
<!----> <!---->
public final void update(byte data)
throws SignatureException
public final void update(byte[] data)
throws SignatureException
public final void update(byte[] data,
int off,
int len)
throws SignatureException
添加要签名的信息
<!----> <!---->
public final byte[] sign()
throws SignatureException
返回签名的数组 , 前提是 initSign 和 update
<!----> <!---->
public final void initVerify(PublicKey publicKey)
throws InvalidKeyException
用指定的公钥初始化
参数 :publicKey 验证时用的公钥
<!----> <!---->
public final boolean verify(byte[] signature)
throws SignatureException
验证签名是否有效 , 前提是已经 initVerify 初始化
参数 : signature 签名数组
*/
<!----> <!---->
import java.security.*;