單鑰密碼體制是一種傳統的加密算法,是指信息的發送方和接收方共同使用同一把密鑰進行加解密。
通常,使用的加密算法 比較簡便高效,密鑰簡短,加解密速度快,破譯極其困難。但是加密的安全性依靠密鑰保管的安全性,在公開的計算機網絡上安全地傳送和保管密鑰是一個嚴峻的問題,并且如果在多用戶的情況下密鑰的保管安全性也是一個問題。
單鑰密碼體制的代表是美國的DES
一個消息摘要就是一個數據塊的數字指紋。即對一個任意長度的一個數據塊進行計算,產生一個唯一指印(對于SHA1是產生一個20字節的二進制數組)。
消息摘要有兩個基本屬性:
- 兩個不同的報文難以生成相同的摘要
- 難以對指定的摘要生成一個報文,而由該報文反推算出該指定的摘要
代表:美國國家標準技術研究所的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函數的特殊要求是:
- 接受的輸入報文數據沒有長度限制;
- 對任何輸入報文數據生成固定長度的摘要(數字指紋)輸出
- 從報文能方便地算出摘要;
- 難以對指定的摘要生成一個報文,而由該報文反推算出該指定的摘要;
- 兩個不同的報文難以生成相同的摘要
代表: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.*; |
- 對于一個用戶來講首先要生成他的密鑰對,并且分別保存
生成一個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();
-
用他私人密鑰(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();
把他的公鑰的信息及簽名發給其它用戶
-
其他用戶用他的公共密鑰(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);
- public key是用X.509編碼的,例碼如下:
- 常用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和updatepublic final void initVerify(PublicKey publicKey)
throws InvalidKeyException
用指定的公鑰初始化
參數:publicKey 驗證時用的公鑰public final boolean verify(byte[] signature)
throws SignatureException
驗證簽名是否有效,前提是已經initVerify初始化
參數: signature 簽名數組*/
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();
// java.security.spec.X509EncodedKeySpec pubX509=new java.security.spec.X509EncodedKeySpec(bX509);
//java.security.spec.X509EncodedKeySpec pubkeyEncode=java.security.spec.X509EncodedKeySpec
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");
// SecureRandom secrand=new SecureRandom();
// secrand.setSeed("tttt".getBytes()); //初始化隨機產生器
// keygen.initialize(576,secrand); //初始化密鑰生成器
keygen.initialize(512);
KeyPair keys=keygen.genKeyPair();
// KeyPair keys=keygen.generateKeyPair(); //生成密鑰組
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();
}
}
首先生成密鑰,并保存(這里并沒的保存的代碼,可參考DSA中的方法)
KeyGenerator keygen = KeyGenerator.getInstance(Algorithm);
SecretKey deskey = keygen.generateKey();
用密鑰加密明文(myinfo),生成密文(cipherByte)
Cipher c1 = Cipher.getInstance(Algorithm);
c1.init(Cipher.ENCRYPT_MODE,deskey);
byte[] cipherByte=c1.doFinal(myinfo.getBytes());
傳送密文和密鑰,本文沒有相應代碼可參考DSA
.............
用密鑰解密密文
c1 = Cipher.getInstance(Algorithm);
c1.init(Cipher.DECRYPT_MODE,deskey);
byte[] clearByte=c1.doFinal(cipherByte);
相對來說對稱密鑰的使用是很簡單的,對于JCE來講支技DES,DESede,Blowfish三種加密術
對于密鑰的保存各傳送可使用對象流或者用二進制編碼,相關參考代碼如下
SecretKey deskey = keygen.generateKey(); |
相關API
KeyGenerator 在DSA中已經說明,在添加JCE后在instance進可以如下參數
DES,DESede,Blowfish,HmacMD5,HmacSHA1
javax.crypto.Cipher 加/解密器
public static final Cipher getInstance(java.lang.String transformation) |
返回一個指定方法的Cipher對象
參數:transformation 方法名(可用 DES,DESede,Blowfish)
public final void init(int opmode, java.security.Key key)
throws java.security.InvalidKeyException
用指定的密鑰和模式初始化Cipher對象
參數:opmode 方式(ENCRYPT_MODE, DECRYPT_MODE, WRAP_MODE,UNWRAP_MODE)
key 密鑰
public final byte[] doFinal(byte[] input) |
對input內的串,進行編碼處理,返回處理后二進制串,是返回解密文還是加解文由init時的opmode決定
注意:本方法的執行前如果有update,是對updat和本次input全部處理,否則是本inout的內容
/* |
公開密鑰密碼體制的奠基人Diffie和Hellman所提出的 "指數密鑰一致協議"(Exponential Key Agreement Protocol),該協議不要求別的安全性 先決條件,允許兩名用戶在公開媒體上交換信息以生成"一致"的,可以共享的密鑰。在JCE的中實現用戶alice生成DH類型的密鑰對,如果長度用1024生成的時間請,推薦第一次生成后保存DHParameterSpec,以便下次使用直接初始化.使其速度加快
System.out.println("ALICE: 產生 DH 對 ..."); |
alice生成公鑰發送組bob
byte[] alicePubKeyEnc = aliceKpair.getPublic().getEncoded(); |
bob從alice發送來的公鑰中讀出DH密鑰對的初始參數生成bob的DH密鑰對
注意這一步一定要做,要保證每個用戶用相同的初始參數生成的
DHParameterSpec dhParamSpec = ((DHPublicKey)alicePubKey).getParams(); |
bob根據alice的公鑰生成本地的DES密鑰
KeyAgreement bobKeyAgree = KeyAgreement.getInstance("DH"); |
bob已經生成了他的DES密鑰,他現把他的公鑰發給alice,
byte[] bobPubKeyEnc = bobKpair.getPublic().getEncoded(); |
alice根據bob的公鑰生成本地的DES密鑰
,,,,,,解碼 |
bob和alice能過這個過程就生成了相同的DES密鑰,在這種基礎就可進行安全能信
常用API
java.security.KeyPairGenerator 密鑰生成器類
public static KeyPairGenerator getInstance(String algorithm)
throws NoSuchAlgorithmException
以指定的算法返回一個KeyPairGenerator 對象
參數: algorithm 算法名.如:原來是DSA,現在添加了
DiffieHellman(DH)
public void initialize(int keysize)
以指定的長度初始化KeyPairGenerator對象,如果沒有初始化系統以1024長度默認設置
參數:keysize 算法位長.其范圍必須在 512 到 1024 之間,且必須為 64
的倍數
注意:如果用1024生長的時間很長,最好生成一次后就保存,下次就不用生成了
public void initialize(AlgorithmParameterSpec params)
throws InvalidAlgorithmParameterException
以指定參數初始化
javax.crypto.interfaces.DHPublicKey
public DHParameterSpec getParams()
返回
java.security.KeyFactory
public static KeyFactory getInstance(String algorithm)
throws NoSuchAlgorithmException
以指定的算法返回一個KeyFactory
參數: algorithm 算法名:DSH,DH
public final PublicKey generatePublic(KeySpec keySpec)
throws InvalidKeySpecException
根據指定的key說明,返回一個PublicKey對象
java.security.spec.X509EncodedKeySpec
public X509EncodedKeySpec(byte[] encodedKey)
根據指定的二進制編碼的字串生成一個key的說明
參數:encodedKey
二進制編碼的字串(一般能過PublicKey.getEncoded()生成)
javax.crypto.KeyAgreement 密碼一至類
public static final KeyAgreement getInstance(java.lang.String
algorithm)
throws java.security.NoSuchAlgorithmException
返回一個指定算法的KeyAgreement對象
參數:algorithm 算法名,現在只能是DiffieHellman(DH)
public final void init(java.security.Key key)
throws java.security.InvalidKeyException
用指定的私鑰初始化
參數:key 一個私鑰
public final java.security.Key doPhase(java.security.Key
key,
boolean lastPhase)
throws java.security.InvalidKeyException,
java.lang.IllegalStateException
用指定的公鑰進行定位,lastPhase確定這是否是最后一個公鑰,對于兩個用戶的
情況下就可以多次定次,最后確定
參數:key 公鑰
lastPhase 是否最后公鑰
public final SecretKey generateSecret(java.lang.String
algorithm)
throws java.lang.IllegalStateException,
java.security.NoSuchAlgorithmException,
java.security.InvalidKeyException
根據指定的算法生成密鑰
參數:algorithm 加密算法(可用 DES,DESede,Blowfish)
*/ |
![]() ![]() |
![]()
|
在加密術中生成密鑰對時,密鑰對的當然是越長越好,但費時也越多,請從中從實際出發選取合適的長度,大部分例碼中的密鑰是每次運行就從新生成,在實際的情 況中是生成后在一段時間保存在文件中,再次運行直接從文件中讀入,從而加快速度。當然定時更新和加強密鑰保管的安全性也是必須的。
![]() |
||
![]() |
王輝,具有八年的編程及系統管理經驗,所使用的語言為C和Java 編程語言。目前在深圳一家公司做程序員,使用C和JAVA為DB2數據庫編程. |