(
史帝芬
, idealist@gcn.net.tw)
1.
密碼學(xué)簡(jiǎn)介
–
加密與解密?
??? 加密是一個(gè)將欲加密的資料用一些數(shù)學(xué)運(yùn)算轉(zhuǎn)成一團(tuán)令人看不懂的東西的過(guò)程
;
解密則是將加密文轉(zhuǎn)換回原始文字的過(guò)程。這個(gè)過(guò)程中,扮演原始文字與加密文字之間轉(zhuǎn)換的數(shù)學(xué)算法稱(chēng)為
Cipher
。
??? 現(xiàn)代的
Cipher
多半會(huì)用
Key
來(lái)加密與解密資料。所謂
Key
是指一個(gè)機(jī)密值,我們可將它視為一通行密碼。加密文字必需使用對(duì)映的
Key
才能解密為原始文字。
A.
對(duì)稱(chēng)型
Cipher
對(duì)稱(chēng)型
Cipher
在傳送端與接收端所用的
Key
是一樣的,
對(duì)稱(chēng)型
Cipher
又叫
Private Key Cipher
,因?yàn)?/span>
Key
的值只有傳送端和接收端知道。如果有第三者知道了
Private Key
值,也就能解開(kāi)加密的資料。
B.
非對(duì)稱(chēng)型
Cipher
非對(duì)稱(chēng)型的
Cipher
又叫
Public Key Cipher
,
Cipher
除了
Private Key
外,還會(huì)引進(jìn)一可以隨意散發(fā)的
Public Key
。被
Public Key
加密的資料只有相對(duì)映的
Private Key
可以解開(kāi),同樣的被
Private Key
加密的資料也只有相對(duì)映的
Public Key
可以解開(kāi)。
?
C.
訊息摘要
(Message Digest)
訊息摘要是從一組輸入資料計(jì)算所得的一個(gè)特別數(shù)字,其原理運(yùn)作就如
hash function
一般。在密碼學(xué)的運(yùn)用里,一般是用來(lái)驗(yàn)證資料是否被竄改。
2. JCE
下載
因?yàn)槊绹?guó)法規(guī)的限制,
Sun
在
JDK
里只提供了少數(shù)的加密方法,其余大部份則只在
SunJCE
里提供,而且
SunJCE
的
API
限制只有美國(guó)、加拿大地區(qū)可以下載。表
1
為
Sun
及
SunJCE
分別支持的加密算法。
?
|
名稱(chēng)
|
型別
|
Sun
|
MD5
|
訊息摘要
|
SHA-1
|
訊息摘要
|
DSA
|
簽章
|
SunJCE
|
HmacMD5
|
MAC
|
HmacSHA1
|
MAC
|
DES
|
對(duì)稱(chēng)型
Cipher
|
DESede
|
非對(duì)稱(chēng)型
Cipher
|
PBEWithMD5AndDES
|
對(duì)稱(chēng)型
Cipher
|
DH
|
Key
的交換
|
表
1 Sun
及
SunJCE
支持的加密算法
雖然美國(guó)法規(guī)有這樣的限定,但是在美國(guó)境外也已經(jīng)有廠商實(shí)作出
JCE
,并且可以在網(wǎng)絡(luò)上直接下載,表
2
就是下載網(wǎng)址的列表。
套件
|
網(wǎng)址
|
免費(fèi)
|
JCE
|
http://java.sun.com/products/jdk/1.2/jce/
|
是
|
Cryptix
|
http://www.cryptix.org/
|
是
|
IAIK
|
http://wwwjce.iaik.tu-graz.ac.at/
|
否
|
表
2 JCE
軟件下載網(wǎng)址
3. JCE
安裝
-
解壓縮到
JDK
目錄下
-
Set ClassPath= C:\JDK\bin\cryptix-jce-api.jar;C:\JDK\bin\cryptix-jce-compat.jar;C:\JDK\bin\cryptix-jce-provider.jar …
-
在
JDK/lib/security/java.security
中加入
security.provider.1=sun.security.provider.Sun (
原來(lái)就有的
)
security.provider.2=cryptix.jce.provider.Cryptix (
加入
)
4.
程序范例
在舉例之前,我先完成一個(gè)公用類(lèi)別,用來(lái)將字符串轉(zhuǎn)成十六進(jìn)制表示法。
public class Msg {
public static String toHexString(byte[] b) {
StringBuffer hexString = new StringBuffer();
String plainText;
for (int i = 0; i < b.length; i++) {
plainText = Integer.toHexString(0xFF & b[i]);
if (plainText.length() < 2) {
plainText = "0" + plainText;
}
hexString.append(plainText);
}
return hexString.toString();
}
}
5.
訊息摘要
(message digest,
以
SHA1
為例
)
產(chǎn)生訊息摘要的步驟
:
-
呼叫
getInstance
取得
MessageDigest
實(shí)體
-
呼叫
update
將資料喂給
MessageDigest
-
呼叫
digest
產(chǎn)生訊息摘要
import java.security.*;
public class SHA extends Object {
public static void main(String[] args) throws Exception
{
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(args[0].getBytes());
byte[] digest = md.digest();
System.out.println(Msg.toHexString(digest));
}
}
ps.
比較兩個(gè)訊息摘要是否相同時(shí),可呼叫
isEqual
。
6.
訊息認(rèn)證碼
(MAC,
以
HmacSHA1
為例
)
訊息認(rèn)證碼只是在產(chǎn)生訊息摘要的過(guò)程里,加進(jìn)一把
key
做為保護(hù),目的是使訊息摘要更難被破解。
產(chǎn)生訊息認(rèn)證碼的步驟
:
-
利用密碼產(chǎn)生一把
key
-
呼叫
getInstance
取得
Mac
實(shí)體
-
呼叫
init
,初始化
Mac
-
呼叫
update
喂資料給
Mac
-
呼叫
doFinal
產(chǎn)生訊息認(rèn)證碼
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
public class MacSHA {
public static void main(String[] args)
{
SecureRandom sr = new SecureRandom();
byte[] keyBytes = new byte[20];
sr.nextBytes(keyBytes);
SecretKey key = new SecretKeySpec(keyBytes, "HmacSHA");
try {
Mac m = Mac.getInstance("HmacSHA");
m.init(key);
m.update(args[0].getBytes());
byte[] mac = m.doFinal();
System.out.println(Msg.toHexString(mac));
}
catch (Exception e) {
System.out.println("Exception!!");
}
}
}
7.
加密與解密
(
以
DES
為例
)
這里舉的加密
/
解密是屬對(duì)稱(chēng)型
Cipher;
在金融交易里,常用對(duì)稱(chēng)型
Cipher
來(lái)加
/
解密資料。
加密
/
解密的步驟
:
-
利用密碼產(chǎn)生一把
key
-
呼叫
getInstance
產(chǎn)生一個(gè)
Cipher
對(duì)象
-
呼叫
init
設(shè)定為加密或解密
-
加密
/
解密
import java.io.*;
import java.security.*;
import javax.crypto.*;
public class PwdDES {
public static final int kBufferSize = 8192;
public static void main(String[] args) throws Exception {
if (args.length < 4) {
System.out.println("Usage: java PwdDES -e|-d passwd inputfile outputfile");
return;
}
//Get or create key.
Key key;
KeyGenerator generator = KeyGenerator.getInstance("DES");
generator.init(new SecureRandom(args[1].getBytes()));
key = generator.generateKey();
//Get a cipher object
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS#5");
//Encrypt or decrypt
if (args[0].indexOf("e") != -1)
cipher.init(Cipher.ENCRYPT_MODE, key);
else
cipher.init(Cipher.DECRYPT_MODE, key);
FileInputStream in = new FileInputStream(args[2]);
FileOutputStream fileOut = new FileOutputStream(args[3]);
CipherOutputStream out = new CipherOutputStream(fileOut, cipher);
byte[] buffer = new byte[kBufferSize];
int length;
while ((length = in.read(buffer)) != -1)
out.write(buffer, 0, length);
in.close();
out.close();
}
}
8.
產(chǎn)生簽章與認(rèn)證
(
以
DSA
為例
)
數(shù)字簽章常用在網(wǎng)絡(luò)上做個(gè)人身份確認(rèn)。
產(chǎn)生簽章的步驟
:
-
呼叫
getInstance
取得一個(gè)
Signature
實(shí)體
-
呼叫
initSign
初始化
Signature
-
呼叫
sign
產(chǎn)生簽章
認(rèn)證的步驟
:
-
呼叫
getInstance
取得一個(gè)
Signature
實(shí)體
-
呼叫
initVerify
初始化
Signature
-
呼叫
verify
認(rèn)證
Sample1:
產(chǎn)生
Private/Public Key
import java.security.*;
import java.security.KeyPairGenerator;
import java.security.KeyPair;
import java.io.*;
public class KeyPair1 {
public static void main(String[] args)
{
try {
KeyPairGenerator genKeyPair = KeyPairGenerator.getInstance("DSA");
genKeyPair.initialize(1024, new SecureRandom());
KeyPair kpKey = genKeyPair.genKeyPair();
PrivateKey prKey = kpKey.getPrivate();
PublicKey puKey = kpKey.getPublic();
ObjectOutputStream osPrivate = new ObjectOutputStream(new FileOutputStream("D:\\Private.Key"));
ObjectOutputStream osPublic = new ObjectOutputStream(new FileOutputStream("D:\\Public.Key"));
osPrivate.writeObject(prKey);
osPublic.writeObject(puKey);
osPrivate.close();
osPublic.close();
}
catch (Exception e) {
System.out.println("Error");
}
}
}
Sample2:
產(chǎn)生簽章與認(rèn)證
import java.io.*;
import java.security.*;
import java.security.Signature;
import java.security.cert.*;
public class GenSign {
public static void main(String[] args) throws Exception {
String options = args[0];
String messagefile = args[1];
String signaturefile = args[2];
Signature signature = Signature.getInstance("DSA");
if (options.indexOf("s") != -1) {
ObjectInputStream is = new ObjectInputStream(new FileInputStream("D:\\Private.key"));
PrivateKey priKey = (PrivateKey) is.readObject();
signature.initSign(priKey);
is.close();
}
else {
ObjectInputStream is = new ObjectInputStream(new FileInputStream("D:\\Public.key"));
PublicKey pubKey = (PublicKey) is.readObject();
signature.initVerify(pubKey);
is.close();
}
FileInputStream in = new FileInputStream(messagefile);
byte[] buffer = new byte[8192];
int length;
while ((length = in.read(buffer))!= -1)
signature.update(buffer, 0, length);
in.close();
if (options.indexOf("s") != -1) {
FileOutputStream out = new FileOutputStream(signaturefile);
byte[] raw = signature.sign();
out.write(raw);
out.close();
}
else {
FileInputStream sigIn = new FileInputStream(signaturefile);
byte[] raw = new byte[sigIn.available()];
sigIn.read(raw);
sigIn.close();
if (signature.verify(raw))
System.out.println("The signature is good.");
else
System.out.println("The signature is bad.");
}
}
}