摘要:
本文主要對數字簽名和消息摘要進行簡要介紹,並通過java實現基本流程。
概念介紹:
消息摘要
一個消息摘要是一個數據塊的數字指紋。即對一個任意長度的一個數據塊進行計算,產生一個唯一指印(對於 SHA1 是產生一個 20 字節的二進制數組)。
消息摘要有兩個基本屬性:
兩個不同的報文難以生成相同的摘要
難以對指定的摘要生成一個報文,而由該報文反推算出該指定的摘要
代表:美國國家標準技術研究所的 SHA1 和麻省理工學院 Ronald Rivest 提出的 MD5
非對稱算法與公鑰體系
1976 年,Dittie 和 Hellman 爲解決密鑰管理問題,在他們的奠基性的工作"密碼學的新方向"一文中,提出一種密鑰交換協議,允許在不安全的媒體上通過通訊雙方交換信息,安全地傳送祕密密鑰。在此新思想的基礎上,很快出現了非對稱密鑰密碼體制,即公鑰密碼體制。在公鑰體制中,加密密鑰不同於解密密鑰,加密密鑰公之於衆,誰都可以使用;解密密鑰只有解密人自己知道。它們分別稱爲公開密鑰(Public key)和祕密密鑰(Private key)。
迄今爲止的所有公鑰密碼體系中,RSA 系統是最著名、最多使用的一種。RSA 公開密鑰密碼系統是由 R.Rivest、A.Shamir 和 L.Adleman 俊教授於 1977 年提出的。RSA 的取名就是來自於這三位發明者的姓的第一個字母
數字簽名
所謂數字簽名就是信息發送者用其私鑰對從所傳報文中提取出的特徵數據(或稱數字指紋)進行 RSA 算法操作,以保證發信人無法抵賴曾發過該信息(即不可抵賴性),同時也確保信息報文在經簽名後末被篡改(即完整性)。當信息接收者收到報文後,就可以用發送者的公鑰對數字簽名進行驗證。
代表:DSA
JAVA實現:
對消息進行消息摘要處理(SHA-1):
java.security.MessageDigest alga= java.security.MessageDigest.getInstance("SHA-1");
alga.update(myinfo.getBytes());
byte[] digesta=alga.digest();
消息發送方生成密鑰組關鍵代碼:
java.security.KeyPairGenerator keygen = java.security.KeyPairGenerator.getInstance("DSA");
keygen.initialize(512);
KeyPair keys = keygen.genKeyPair();// 生成密鑰組(包括私鑰和公鑰)
PublicKey pubkey = keys.getPublic();
PrivateKey prikey = keys.getPrivate();
消息發送方通過私鑰對信息進行數字簽名:
java.security.Signature signet = java.security.Signature.getInstance("DSA");
signet.initSign(myprikey); //通過私鑰初始化數字簽名
signet.update(myinfo.getBytes()); //寫入要發送信息的字節數組
byte[] signed = signet.sign(); // 對信息的數字簽名
消息接收方對生成的數字簽名通過公鑰進行驗籤:
java.security.Signature signetcheck = java.security.Signature.getInstance("DSA");
signetcheck.initVerify(pubkey); //通過公鑰初始化驗籤
signetcheck.update(info.getBytes()); //對收到的信息進行寫入
signetcheck.verify(signed);//驗籤,signed爲上面私鑰加密後的數字簽名
測試代碼:
消息摘要:
package javaTest;
import java.security.MessageDigest;
public class MyDigest {
public static void main(String[] args) {
MyDigest my=new MyDigest();
my.testDigest();
}
public void testDigest()
{
try {
String myinfo="我的測試信息";
MessageDigest alga= MessageDigest.getInstance("SHA-1");
alga.update(myinfo.getBytes());
byte[] digesta=alga.digest();
System.out.println("本信息摘要是 :"+byte2hex(digesta));
// 通過某種方式傳給其他人你的信息 (myinfo) 和摘要 (digesta) 對方可以判斷是否更改或傳輸正常
MessageDigest algb=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();
}
}
數字簽名
package javaTest;
import java.security.*;
import java.security.spec.*;
public class testdsa {
public static void main(String[] args) throws java.security.NoSuchAlgorithmException,
java.lang.Exception {
testdsa my = new testdsa();
my.run();
}
public void run() {
// 數字簽名生成密鑰
// 第一步生成密鑰對 , 如果已經生成過 , 本過程就可以跳過 ,
// 對用戶來講 myprikey.dat 要保存在本地
// 而 mypubkey.dat 給發佈給其它用戶
if ((new java.io.File("myprikey.dat")).exists() == false) {
if (generatekey() == false) {
System.out.println("生成密鑰對敗");
return;
}
;
}
// 第二步 , 此用戶
// 從文件中讀入私鑰 , 對一個字符串進行簽名後保存在一個文件 (myinfo.dat) 中
// 並且再把 myinfo.dat 發送出去
// 爲了方便數字簽名也放進了 myifno.dat 文件中 , 當然也可分別發送
try {
java.io.ObjectInputStream in = new java.io.ObjectInputStream(
new java.io.FileInputStream("myprikey.dat"));
PrivateKey myprikey = (PrivateKey) in.readObject();
in.close();
String myinfo = "這是我的信息"; // 要簽名的信息
// 用私鑰對信息生成數字簽名
java.security.Signature signet = java.security.Signature.getInstance("DSA");
signet.initSign(myprikey);
signet.update(myinfo.getBytes());
byte[] signed = signet.sign(); // 對信息的數字簽名
System.out.println("signed( 簽名內容 )=" + byte2hex(signed));
// 把信息和數字簽名保存在一個文件中
java.io.ObjectOutputStream out = new java.io.ObjectOutputStream(
new java.io.FileOutputStream("myinfo.dat"));
out.writeObject(myinfo);
out.writeObject(signed);
out.close();
System.out.println("簽名並生成文件成功");
} catch (java.lang.Exception e) {
e.printStackTrace();
System.out.println("簽名並生成文件失敗");
}
// 第三步
// 其他人通過公共方式得到此戶的公鑰和文件
// 其他人用此戶的公鑰 , 對文件進行檢查 , 如果成功說明是此用戶發佈的信息 .
try {
java.io.ObjectInputStream in = new java.io.ObjectInputStream(
new java.io.FileInputStream("mypubkey.dat"));
PublicKey pubkey = (PublicKey) in.readObject();
in.close();
System.out.println(pubkey.getFormat());
in = new java.io.ObjectInputStream(new java.io.FileInputStream("myinfo.dat"));
String info = (String) in.readObject();
byte[] signed = (byte[]) in.readObject();
in.close();
java.security.Signature signetcheck = java.security.Signature.getInstance("DSA");
signetcheck.initVerify(pubkey);
signetcheck.update(info.getBytes());
if (signetcheck.verify(signed)) {
System.out.println("info=" + info);
System.out.println("簽名正常");
} else System.out.println("非簽名正常");
} catch (java.lang.Exception e) {
e.printStackTrace();
}
}
// 生成一對文件 myprikey.dat 和 mypubkey.dat--- 私鑰和公鑰 ,
// 公鑰要用戶發送 ( 文件 , 網絡等方法 ) 給其它用戶 , 私鑰保存在本地
public boolean generatekey() {
try {
java.security.KeyPairGenerator keygen =
java.security.KeyPairGenerator.getInstance("DSA");
keygen.initialize(512);
KeyPair keys = keygen.genKeyPair();// 生成密鑰組
PublicKey pubkey = keys.getPublic();
PrivateKey prikey = keys.getPrivate();
java.io.ObjectOutputStream out = new java.io.ObjectOutputStream(
new java.io.FileOutputStream("myprikey.dat"));
out.writeObject(prikey);
out.close();
System.out.println("寫入對象 prikeys ok");
out = new java.io.ObjectOutputStream(
new java.io.FileOutputStream("mypubkey.dat"));
out.writeObject(pubkey);
out.close();
System.out.println("寫入對象 pubkeys ok");
System.out.println("生成密鑰對成功");
return true;
} catch (java.lang.Exception e) {
e.printStackTrace();
System.out.println("生成密鑰對失敗");
return false;
}
}
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();
}
}