1 RSA算法
1.1 定義
RSA,這種算法1978年就出現(xiàn)了,它是第一個(gè)既能用于數(shù)據(jù)加密也能用于數(shù)字簽名的算法。它易于理解和操作,也很流行。算法的名字以發(fā)明者的名字命名:Ron Rivest, AdiShamir 和Leonard Adleman。
這種加密算法的特點(diǎn)主要是密鑰的變化,DES只有一個(gè)密鑰。相當(dāng)于只有一把鑰匙,如果這把鑰匙丟了,數(shù)據(jù)也就不安全了。但是RSA同時(shí)有兩把鑰匙,公鑰與私鑰。同時(shí)支持?jǐn)?shù)字簽名。數(shù)字簽名的意義在于,對(duì)傳輸過來的數(shù)據(jù)進(jìn)行校驗(yàn)。確保數(shù)據(jù)在傳輸工程中不被修改。
RSA算法一直是最廣為使用的非對(duì)稱加密算法。毫不夸張地說,只要有計(jì)算機(jī)網(wǎng)絡(luò)的地方,就有RSA算法。
這種算法非??煽?,密鑰越長,它就越難破解。根據(jù)已經(jīng)披露的文獻(xiàn),目前被破解的最長RSA密鑰是768個(gè)二進(jìn)制位。也就是說,長度超過768位的密鑰,還無法破解(至少?zèng)]人公開宣布)。因此可以認(rèn)為,1024位的RSA密鑰基本安全,2048位的密鑰極其安全。
1.2 原理
流程分析:
甲方構(gòu)建密鑰對(duì)兒,將公鑰公布給乙方,將私鑰保留。
甲方使用私鑰加密數(shù)據(jù),然后用私鑰對(duì)加密后的數(shù)據(jù)簽名,發(fā)送給乙方簽名以及加密后的數(shù)據(jù);乙方使用公鑰、簽名來驗(yàn)證待解密數(shù)據(jù)是否有效,如果有效使用公鑰對(duì)數(shù)據(jù)解密。
乙方使用公鑰加密數(shù)據(jù),向甲方發(fā)送經(jīng)過加密后的數(shù)據(jù);甲方獲得加密數(shù)據(jù),通過私鑰解密
1.2.1 互質(zhì)關(guān)系
如果兩個(gè)正整數(shù),除了1以外,沒有其他公因子,我們就稱這兩個(gè)數(shù)是互質(zhì)關(guān)系(coprime)。比如,15和32沒有公因子,所以它們是互質(zhì)關(guān)系。這說明,不是質(zhì)數(shù)也可以構(gòu)成互質(zhì)關(guān)系。
關(guān)于互質(zhì)關(guān)系,不難得到以下結(jié)論:
- 任意兩個(gè)質(zhì)數(shù)構(gòu)成互質(zhì)關(guān)系,比如13和61。
- 一個(gè)數(shù)是質(zhì)數(shù),另一個(gè)數(shù)只要不是前者的倍數(shù),兩者就構(gòu)成互質(zhì)關(guān)系,比如3和10。
- 如果兩個(gè)數(shù)之中,較大的那個(gè)數(shù)是質(zhì)數(shù),則兩者構(gòu)成互質(zhì)關(guān)系,比如97和57。
- 1和任意一個(gè)自然數(shù)是都是互質(zhì)關(guān)系,比如1和99。
- p是大于1的整數(shù),則p和p-1構(gòu)成互質(zhì)關(guān)系,比如57和56。
- p是大于1的奇數(shù),則p和p-2構(gòu)成互質(zhì)關(guān)系,比如17和15
1.2.2 歐拉函數(shù)
請(qǐng)思考以下問題:
任意給定正整數(shù)n,請(qǐng)問在小于等于n的正整數(shù)之中,有多少個(gè)與n構(gòu)成互質(zhì)關(guān)系?(比如,在1到8之中,有多少個(gè)數(shù)與8構(gòu)成互質(zhì)關(guān)系?)
計(jì)算這個(gè)值的方法就叫做歐拉函數(shù),以φ(n)表示。在1到8之中,與8形成互質(zhì)關(guān)系的是1、3、5、7,所以 φ(n) = 4
φ(n) 的計(jì)算方法并不復(fù)雜,但是為了得到最后那個(gè)公式,需要一步步討論。
第一種情況
如果n=1,則 φ(1) = 1。因?yàn)?與任何數(shù)(包括自身)都構(gòu)成互質(zhì)關(guān)系。
第二種情況
如果n是質(zhì)數(shù),則 φ(n)=n-1 。因?yàn)橘|(zhì)數(shù)與小于它的每一個(gè)數(shù),都構(gòu)成互質(zhì)關(guān)系。比如5與1、2、3、4都構(gòu)成互質(zhì)關(guān)系。
第三種情況
如果n是質(zhì)數(shù)的某一個(gè)次方,即 n = p^k (p為質(zhì)數(shù),k為大于等于1的整數(shù)),則

比如
φ(8) = φ(2^3) =2^3 - 2^2 = 8 -4 = 4這是因?yàn)橹挥挟?dāng)一個(gè)數(shù)不包含質(zhì)數(shù)
p,才可能與n互質(zhì)。而包含質(zhì)數(shù)p的數(shù)一共有p^(k-1)個(gè),即1×p、2×p、3×p、...、p^(k-1)×p,把它們?nèi)コO碌木褪桥cn互質(zhì)的數(shù)。上面的式子還可以寫成下面的形式:

可以看出,上面的第二種情況是
k=1 時(shí)的特例。第四種情況
如果
n可以分解成兩個(gè)互質(zhì)的整數(shù)之積,n = p1 × p2則
φ(n) = φ(p1p2) = φ(p1)φ(p2)即
積的歐拉函數(shù)等于各個(gè)因子的歐拉函數(shù)之積。比如,φ(56)=φ(8×7)=φ(8)×φ(7)=4×6=24這一條的證明要用到"中國剩余定理",這里就不展開了,只簡單說一下思路:如果
a與p1互質(zhì)(a<p1),b與p2互質(zhì)(b<p2),c與p1p2互質(zhì)(c<p1p2),則c與數(shù)對(duì) (a,b) 是一一對(duì)應(yīng)關(guān)系。由于a的值有φ(p1)種可能,b的值有φ(p2)種可能,則數(shù)對(duì) (a,b) 有φ(p1)φ(p2)種可能,而c的值有φ(p1p2)種可能,所以φ(p1p2)就等于φ(p1)φ(p2)。第五種情況
因?yàn)槿我庖粋€(gè)大于1的正整數(shù),都可以寫成一系列質(zhì)數(shù)的積。

根據(jù)第4條的結(jié)論,得到

再根據(jù)第3條的結(jié)論,得到

也就等于

這就是歐拉函數(shù)的通用計(jì)算公式。比如,1323的歐拉函數(shù),計(jì)算過程如下:

1.2.3 歐拉定理
歐拉函數(shù)的用處,在于歐拉定理。歐拉定理指的是:
如果兩個(gè)正整數(shù)a和n互質(zhì),則n的歐拉函數(shù) φ(n) 可以讓下面的等式成立:

也就是說,
a的φ(n)次方被n除的余數(shù)為1。或者說,a的φ(n)次方減去1,可以被n整除。比如,3和7互質(zhì),而7的歐拉函數(shù)φ(7)等于6,所以3的6次方(729)減去1,可以被7整除(728/7=104)。歐拉定理的證明比較復(fù)雜,這里就省略了。我們只要記住它的結(jié)論就行了。
歐拉定理可以大大簡化某些運(yùn)算。比如,7和10互質(zhì),根據(jù)歐拉定理,

已知
φ(10) 等于4,所以馬上得到7的4倍數(shù)次方的個(gè)位數(shù)肯定是1。
因此,7的任意次方的個(gè)位數(shù)(例如7的222次方),心算就可以算出來。
歐拉定理有一個(gè)特殊情況。
假設(shè)正整數(shù)
a與質(zhì)數(shù)p互質(zhì),因?yàn)橘|(zhì)數(shù)p的φ(p)等于p-1,則歐拉定理可以寫成
這就是著名的費(fèi)馬小定理。它是歐拉定理的特例。
歐拉定理是RSA算法的核心。理解了這個(gè)定理,就可以理解RSA。
1.2.4 模反元素
還剩下最后一個(gè)概念:
如果兩個(gè)正整數(shù)a和n互質(zhì),那么一定可以找到整數(shù)b,使得 ab-1 被n整除,或者說ab被n除的余數(shù)是1。

這時(shí),b就叫做a的
模反元素比如,3和11互質(zhì),那么3的模反元素就是
4,因?yàn)?(3 × 4)-1 可以被11整除。顯然,模反元素不止一個(gè), 4加減11的整數(shù)倍都是3的模反元素 {...,-18,-7,4,15,26,...},即如果b是a的模反元素,則 b+kn 都是a的模反元素。歐拉定理可以用來證明模反元素必然存在。
可以看到,a的 φ(n)-1 次方,就是a的模反元素。
1.3 實(shí)際操作
下面代碼用到的常量:
static BASE64Decoder de = new BASE64Decoder();
static BASE64Encoder en = new BASE64Encoder();
public static final String KEY_ALGORITHM = "RSA";
public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
private static final String PUBLIC_KEY = "RSAPublicKey";
private static final String PRIVATE_KEY = "RSAPrivateKey";
1.3.1 生成公私鑰并獲取
/**
* 初始化密鑰
*/
public static Map<String, Object> initKey() throws Exception {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
keyPairGen.initialize(1024);
KeyPair keyPair = keyPairGen.generateKeyPair();
// 公鑰
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
// 私鑰
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
Map<String, Object> keyMap = new HashMap<String, Object>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 取得私鑰
* @throws Exception
*/
public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return en.encode(key.getEncoded());
}
/**
* 取得公鑰
*/
public static String getPublicKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return en.encode(key.getEncoded());
}
1.3.2 公私鑰加簽驗(yàn)簽
/**
* 用私鑰對(duì)信息生成數(shù)字簽名
* @param data 加密數(shù)據(jù)
* @param privateKey 私鑰
*/
public static String sign(byte[] data, String privateKey) throws Exception {
// 解密由base64編碼的私鑰
byte[] keyBytes = de.decodeBuffer(privateKey);
// 構(gòu)造PKCS8EncodedKeySpec對(duì)象
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
// KEY_ALGORITHM 指定的加密算法
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 取私鑰匙對(duì)象
PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 用私鑰對(duì)信息生成數(shù)字簽名
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initSign(priKey);
signature.update(data);
return en.encode(signature.sign());
}
/**
* 公鑰校驗(yàn)數(shù)字簽名
* @param data 加密數(shù)據(jù)
* @param publicKey 公鑰
* @param sign 數(shù)字簽名
* @return 校驗(yàn)成功返回true 失敗返回false
*/
public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {
// 解密由base64編碼的公鑰
byte[] keyBytes = de.decodeBuffer(publicKey);
// 構(gòu)造X509EncodedKeySpec對(duì)象
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
// KEY_ALGORITHM 指定的加密算法
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
// 取公鑰匙對(duì)象
PublicKey pubKey = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initVerify(pubKey);
signature.update(data);
// 驗(yàn)證簽名是否正常
return signature.verify(de.decodeBuffer(sign));
}
1.3.3 公私鑰加密解密
/**
* 加密
* 用私鑰加密
* @param data
* @param key
*/
public static String decryptByPrivateKey(byte[] data, String key) throws Exception {
// 對(duì)密鑰解密
byte[] keyBytes = de.decodeBuffer(key);
// 取得私鑰
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 對(duì)數(shù)據(jù)解密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(cipher.doFinal(data));
}
/**
* 加密
* 用公鑰加密
* @param data
* @param key
*/
public static String decryptByPublicKey(byte[] data, String key) throws Exception {
// 對(duì)密鑰解密
byte[] keyBytes = de.decodeBuffer(key);
// 取得公鑰
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
// 對(duì)數(shù)據(jù)解密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return new String(cipher.doFinal(data));
}
/**
* 加密<br>
* 用公鑰加密
* @param data
* @param key
* @return
* @throws Exception
*/
public static String encryptByPublicKey(byte[] data, String key) throws Exception {
// 對(duì)公鑰解密
byte[] keyBytes = de.decodeBuffer(key);
// 取得公鑰
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicKey = keyFactory.generatePublic(x509KeySpec);
// 對(duì)數(shù)據(jù)加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return en.encode(cipher.doFinal(data));
}
/**
* 加密<br>
* 用私鑰加密
*
* @param data
* @param key
* @return
* @throws Exception
*/
public static String encryptByPrivateKey(byte[] data, String key) throws Exception {
// 對(duì)密鑰解密
byte[] keyBytes = de.decodeBuffer(key);
// 取得私鑰
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 對(duì)數(shù)據(jù)加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return en.encode(cipher.doFinal(data));
}
1.3.4 結(jié)果驗(yàn)證
public static void main(String[] args) throws Exception{
//生成 公鑰私鑰
Map<String, Object> keyMap = initKey();
String publicKey = getPublicKey(keyMap);
String privateKey = getPrivateKey(keyMap);
System.err.println("公鑰: \n\r" + publicKey);
System.err.println("私鑰: \n\r" + privateKey);
System.err.println("公鑰加密——私鑰解密");
String inputStr = "abc";
byte[] data = inputStr.getBytes();
String encodedData = encryptByPublicKey(data, publicKey);
String decodedData = decryptByPrivateKey(de.decodeBuffer(encodedData),privateKey);
System.err.println("加密前: " + inputStr +"\n\r" +"加密中: "+encodedData+"\n\r" + "解密后: " + decodedData);
System.err.println("公鑰加密——私鑰解密");
System.err.println("私鑰加密——公鑰解密");
String inputStr_new = "sign";
byte[] data_new = inputStr_new.getBytes();
String encodedData_new = encryptByPrivateKey(data_new, privateKey);
String decodedData_new =decryptByPublicKey(de.decodeBuffer(encodedData_new), publicKey);
System.err.println("加密前: " + inputStr_new + "\n\r" +"加密中: "+encodedData_new+"\n\r" + "解密后: " + decodedData_new);
System.err.println("私鑰加密——公鑰解密");
System.err.println("私鑰簽名——公鑰驗(yàn)證簽名");
// 產(chǎn)生簽名
String sign = sign(encodedData_new.getBytes(), privateKey);
System.err.println("簽名:\r\n" + sign);
// 驗(yàn)證簽名
boolean status = verify(encodedData_new.getBytes(), publicKey, sign);
System.err.println("狀態(tài):\r\n" + status);
System.err.println("私鑰簽名——公鑰驗(yàn)證簽名");
}
2 DH算法
2.1 定義
接下來我們分析DH加密算法,一種適基于密鑰一致協(xié)議的加密算法。
Diffie-Hellman算法(D-H算法),密鑰一致協(xié)議,非對(duì)稱加密算法。
是由公開密鑰密碼體制的奠基人Diffie和Hellman所提出的一種思想。簡單的說就是允許兩名用戶在公開媒體上交換信息以生成一致的、可以共享的密鑰。換句話說,就是由甲方產(chǎn)出一對(duì)密鑰(公鑰、私鑰),乙方依照甲方公鑰產(chǎn)生乙方密鑰對(duì)(公鑰、私鑰)。以此為基線,作為數(shù)據(jù)傳輸保密基礎(chǔ),同時(shí)雙方使用同一種對(duì)稱加密算法構(gòu)建本地密鑰(SecretKey)對(duì)數(shù)據(jù)加密。這樣,在互通了本地密鑰(SecretKey)算法后,甲乙雙方公開自己的公鑰,使用對(duì)方的公鑰和剛才產(chǎn)生的私鑰加密數(shù)據(jù),同時(shí)可以使用對(duì)方的公鑰和自己的私鑰對(duì)數(shù)據(jù)解密。不單單是甲乙雙方兩方,可以擴(kuò)展為多方共享數(shù)據(jù)通訊,這樣就完成了網(wǎng)絡(luò)交互數(shù)據(jù)的安全通訊!該算法源于中國的同余定理——中國馀數(shù)定理
流程分析:
- 甲方構(gòu)建密鑰對(duì)兒,將公鑰公布給乙方,將私鑰保留;雙方約定數(shù)據(jù)加密算法;乙方通過甲方公鑰構(gòu)建密鑰對(duì)兒,將公鑰公布給甲方,將私鑰保留。
- 甲方使用私鑰、乙方公鑰、約定數(shù)據(jù)加密算法構(gòu)建本地密鑰,然后通過本地密鑰加密數(shù)據(jù),發(fā)送給乙方加密后的數(shù)據(jù);乙方使用私鑰、甲方公鑰、約定數(shù)據(jù)加密算法構(gòu)建本地密鑰,然后通過本地密鑰對(duì)數(shù)據(jù)解密。
- 乙方使用私鑰、甲方公鑰、約定數(shù)據(jù)加密算法構(gòu)建本地密鑰,然后通過本地密鑰加密數(shù)據(jù),發(fā)送給甲方加密后的數(shù)據(jù);甲方使用私鑰、乙方公鑰、約定數(shù)據(jù)加密算法構(gòu)建本地密鑰,然后通過本地密鑰對(duì)數(shù)據(jù)解密。
2.2 實(shí)際操作
下面代碼用到的常量
/**
* 默認(rèn)密鑰字節(jié)數(shù)
* Default Keysize 1024
*/
private static final int KEY_SIZE = 1024;
public static final String ALGORITHM = "DH";
/**
* DH加密下需要一種對(duì)稱加密算法對(duì)數(shù)據(jù)加密,這里我們使用DES,也可以使用其他對(duì)稱加密算法。
*/
public static final String SECRET_ALGORITHM = "DES";
private static final String PUBLIC_KEY = "DHPublicKey";
private static final String PRIVATE_KEY = "DHPrivateKey";
static BASE64Decoder de = new BASE64Decoder();
static BASE64Encoder en = new BASE64Encoder();
2.2.1 生成甲乙公私鑰
2.2.1.1 生成甲公私鑰
/**
* 初始化甲方密鑰
*/
public static Map<String, Object> initKey() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
keyPairGenerator.initialize(KEY_SIZE);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 甲方公鑰
DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();
// 甲方私鑰
DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();
Map<String, Object> keyMap = new HashMap<String, Object>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
2.2.1.2 根據(jù)甲公鑰生成乙公私鑰
/**
* 使用甲方公鑰====初始化乙方密鑰
*/
public static Map<String, Object> initKey(String key) throws Exception {
// 解析甲方公鑰
byte[] keyBytes = de.decodeBuffer(key);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
// 由甲方公鑰====構(gòu)建乙方密鑰
DHParameterSpec dhParamSpec = ((DHPublicKey) pubKey).getParams();
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(keyFactory.getAlgorithm());
keyPairGenerator.initialize(dhParamSpec);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 乙方公鑰
DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();
// 乙方私鑰
DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();
Map<String, Object> keyMap = new HashMap<String, Object>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
2.2.1.3 獲取公私鑰
/**
* 取得私鑰
*/
public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return en.encode(key.getEncoded());
}
/**
* 取得公鑰
*/
public static String getPublicKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return en.encode((key.getEncoded()));
}
2.2.2 根據(jù)公私鑰生成本地密鑰
/**
* 構(gòu)建密鑰
*
* @param publicKey 公鑰
* @param privateKey 私鑰
*/
private static SecretKey getSecretKey(String publicKey, String privateKey) throws Exception {
// 初始化公鑰
byte[] pubKeyBytes = de.decodeBuffer(publicKey);
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(pubKeyBytes);
PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
// 初始化私鑰
byte[] priKeyBytes = de.decodeBuffer(privateKey);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(priKeyBytes);
Key priKey = keyFactory.generatePrivate(pkcs8KeySpec);
KeyAgreement keyAgree = KeyAgreement.getInstance(keyFactory.getAlgorithm());
//用自己的私鑰初始化keyAgreement
keyAgree.init(priKey);
keyAgree.doPhase(pubKey, true);
// 生成本地密鑰
SecretKey secretKey = keyAgree.generateSecret(SECRET_ALGORITHM);
return secretKey;
}
2.2.3 加密解密
/**
* 加密<br>
* @param data 待加密數(shù)據(jù)
* @param publicKey 甲方公鑰
* @param privateKey 乙方私鑰
*/
public static String encrypt(byte[] data, String publicKey,String privateKey) throws Exception {
// 生成本地密鑰
SecretKey secretKey = getSecretKey(publicKey, privateKey);
// 數(shù)據(jù)加密
Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return en.encode(cipher.doFinal(data));
}
/**
* 解密<br>
*
* @param data 待解密數(shù)據(jù)
* @param publicKey 乙方公鑰
* @param privateKey 乙方私鑰
*/
public static String decrypt(byte[] data, String publicKey,String privateKey) throws Exception {
// 生成本地密鑰
SecretKey secretKey = getSecretKey(publicKey, privateKey);
// 數(shù)據(jù)解密
Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return new String(cipher.doFinal(data));
}
2.2.4 結(jié)果驗(yàn)證
public static void main(String[] args)throws Exception {
// 生成甲方密鑰對(duì)兒
Map<String, Object> aKeyMap = initKey();
String aPublicKey = getPublicKey(aKeyMap);
String aPrivateKey = getPrivateKey(aKeyMap);
System.err.println("甲方公鑰:\r\n" + aPublicKey);
System.err.println("甲方私鑰:\r\n" + aPrivateKey);
// 由甲方公鑰產(chǎn)生本地密鑰對(duì)兒
Map<String, Object> bKeyMap = initKey(aPublicKey);
String bPublicKey = getPublicKey(bKeyMap);
String bPrivateKey = getPrivateKey(bKeyMap);
System.err.println("乙方公鑰:\r\n" + bPublicKey);
System.err.println("乙方私鑰:\r\n" + bPrivateKey);
String aInput = "abc ";
System.err.println("原文: " + aInput);
// 由甲方公鑰,乙方私鑰構(gòu)建密文
String aCode = encrypt(aInput.getBytes(), aPublicKey,bPrivateKey);
System.err.println("由甲方公鑰,乙方私鑰構(gòu)建密文: \r\n" + aCode);
// 由乙方公鑰,甲方私鑰解密
String aDecode = decrypt(de.decodeBuffer(aCode), bPublicKey, aPrivateKey);
System.err.println("由乙方公鑰,甲方私鑰解密: \r\n" + aDecode);
System.err.println(" ===============反過來加密解密================== ");
String bInput = "def ";
System.err.println("原文: " + bInput);
// 由乙方公鑰,甲方私鑰構(gòu)建密文
String bCode = encrypt(bInput.getBytes(), bPublicKey,aPrivateKey);
System.err.println("由乙方公鑰,甲方私鑰構(gòu)建密文: \r\n" + bCode);
// 由甲方公鑰,乙方私鑰解密
String bDecode = decrypt(de.decodeBuffer(bCode), aPublicKey, bPrivateKey);
System.err.println("由甲方公鑰,乙方私鑰解密: \r\n" + bDecode);
}
3 DSA算法
3.1 定義
接下來我們介紹DSA數(shù)字簽名,非對(duì)稱加密的另一種實(shí)現(xiàn)。
DSA:DSA-Digital Signature Algorithm是Schnorr和ElGamal簽名算法的變種,被美國NIST作為DSS(DigitalSignature Standard)。簡單的說,這是一種更高級(jí)的驗(yàn)證方式,用作數(shù)字簽名。不單單只有公鑰、私鑰,還有數(shù)字簽名。私鑰加密生成數(shù)字簽名,公鑰驗(yàn)證數(shù)據(jù)及簽名。如果數(shù)據(jù)和簽名不匹配則認(rèn)為驗(yàn)證失敗!數(shù)字簽名的作用就是校驗(yàn)數(shù)據(jù)在傳輸過程中不被修改。數(shù)字簽名,是單向加密的升級(jí)
3.2 實(shí)際操作
用到的常量
public static final String ALGORITHM = "DSA";
/**
* 默認(rèn)密鑰字節(jié)數(shù)
*/
private static final int KEY_SIZE = 1024;
/**
* 默認(rèn)種子
*/
private static final String DEFAULT_SEED = "0f22507a10bbddd07d8a3082122966e3";
private static final String PUBLIC_KEY = "DSAPublicKey";
private static final String PRIVATE_KEY = "DSAPrivateKey";
static BASE64Decoder de = new BASE64Decoder();
static BASE64Encoder en = new BASE64Encoder();
3.2.1 生成公私鑰并獲取
/**
* 生成密鑰
*
* @param seed 種子
* @return 密鑰對(duì)象
* @throws Exception
*/
public static Map<String, Object> initKey(String seed) throws Exception {
KeyPairGenerator keygen = KeyPairGenerator.getInstance(ALGORITHM);
// 初始化隨機(jī)產(chǎn)生器
SecureRandom secureRandom = new SecureRandom();
secureRandom.setSeed(seed.getBytes());
keygen.initialize(KEY_SIZE, secureRandom);
KeyPair keys = keygen.genKeyPair();
DSAPublicKey publicKey = (DSAPublicKey) keys.getPublic();
DSAPrivateKey privateKey = (DSAPrivateKey) keys.getPrivate();
Map<String, Object> map = new HashMap<String, Object>(2);
map.put(PUBLIC_KEY, publicKey);
map.put(PRIVATE_KEY, privateKey);
return map;
}
/**
* 取得私鑰
*/
public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return en.encode(key.getEncoded());
}
/**
* 取得公鑰
*/
public static String getPublicKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return en.encode(key.getEncoded());
}
3.2.2 加簽驗(yàn)簽
3.2.2.1 私鑰加簽
/**
* 用私鑰對(duì)信息生成數(shù)字簽名
* @param data 加密數(shù)據(jù)
* @param privateKey 私鑰
*/
public static String sign(byte[] data, String privateKey) throws Exception {
// 解密由base64編碼的私鑰
byte[] keyBytes = de.decodeBuffer(privateKey);
// 構(gòu)造PKCS8EncodedKeySpec對(duì)象
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
// KEY_ALGORITHM 指定的加密算法
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
// 取私鑰匙對(duì)象
PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 用私鑰對(duì)信息生成數(shù)字簽名
Signature signature = Signature.getInstance(keyFactory.getAlgorithm());
signature.initSign(priKey);
signature.update(data);
return en.encode(signature.sign());
}
3.2.2.1 公鑰驗(yàn)簽
/**
* 校驗(yàn)數(shù)字簽名
*/
public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {
// 解密由base64編碼的公鑰
byte[] keyBytes = de.decodeBuffer(publicKey);
// 構(gòu)造X509EncodedKeySpec對(duì)象
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
// ALGORITHM 指定的加密算法
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
// 取公鑰匙對(duì)象
PublicKey pubKey = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance(keyFactory.getAlgorithm());
signature.initVerify(pubKey);
signature.update(data);
// 驗(yàn)證簽名是否正常
return signature.verify(de.decodeBuffer(sign));
}
3.2.3 結(jié)果驗(yàn)證
public static void main(String[] args) throws Exception{
String inputStr = "abc";
byte[] data = inputStr.getBytes();
// 構(gòu)建密鑰
Map<String, Object> keyMap = initKey(DEFAULT_SEED);
// 獲得密鑰
String publicKey = getPublicKey(keyMap);
String privateKey = getPrivateKey(keyMap);
System.err.println("公鑰:\r\n" + publicKey);
System.err.println("私鑰:\r\n" + privateKey);
// 產(chǎn)生簽名
String sign = sign(data, privateKey);
System.err.println("簽名:\r\n" + sign);
// 驗(yàn)證簽名
boolean status = verify(data, publicKey, sign);
System.err.println("狀態(tài):\r\n" + status);
}
4 ECC
4.1 定義
ECC非對(duì)稱加密算法最高級(jí)
ECC-Elliptic Curves Cryptography,橢圓曲線密碼編碼學(xué),是目前已知的公鑰體制中,對(duì)每比特所提供加密強(qiáng)度最高的一種體制。在軟件注冊(cè)保護(hù)方面起到很大的作用,一般的序列號(hào)通常由該算法產(chǎn)生。
ECC算法在jdk1.5后加入支持,目前僅僅只能完成密鑰的生成與解析。
如果想要獲得ECC算法實(shí)現(xiàn),需要調(diào)用硬件完成加密/解密( ECC算法相當(dāng)耗費(fèi)資源 ,如果單純使用CPU進(jìn)行加密/解密,效率低下)
4.2 實(shí)際操作
注意:Chipher不支持EC算法 ,以下代碼僅供參考。Chipher、Signature、KeyPairGenerator、KeyAgreement、SecretKey均不支持EC算法。為了確保程序能夠正常執(zhí)行,使用了NullCipher類,驗(yàn)證程序
其中的KeyFactory.getInstance("EC", "SunEC");是參考ECKeyFactory源碼
如下代碼用到的常量
public static final String ALGORITHM = "EC";
private static final String PUBLIC_KEY = "ECCPublicKey";
private static final String PRIVATE_KEY = "ECCPrivateKey";
static BASE64Decoder de = new BASE64Decoder();
static BASE64Encoder en = new BASE64Encoder();
4.2.1 生成公私鑰并獲取
/**
* 初始化密鑰
*
* @return
* @throws Exception
*/
public static Map<String, Object> initKey() throws Exception {
BigInteger x1 = new BigInteger("2fe13c0537bbc11acaa07d793de4e6d5e5c94eee8", 16);
BigInteger x2 = new BigInteger("289070fb05d38ff58321f2e800536d538ccdaa3d9", 16);
ECPoint g = new ECPoint(x1, x2);
// the order of generator
BigInteger n = new BigInteger("5846006549323611672814741753598448348329118574063", 10);
// the cofactor
int h = 2;
int m = 163;
int[] ks = {7, 6, 3};
ECFieldF2m ecField = new ECFieldF2m(m, ks);
// y^2+xy=x^3+x^2+1
BigInteger a = new BigInteger("1", 2);
BigInteger b = new BigInteger("1", 2);
EllipticCurve ellipticCurve = new EllipticCurve(ecField, a, b);
ECParameterSpec ecParameterSpec = new ECParameterSpec(ellipticCurve, g, n, h);
// 公鑰
ECPublicKey publicKey = new ECPublicKeyImpl(g, ecParameterSpec);
BigInteger s = new BigInteger("1234006549323611672814741753598448348329118574063", 10);
// 私鑰
ECPrivateKey privateKey = new ECPrivateKeyImpl(s, ecParameterSpec);
Map<String, Object> keyMap = new HashMap<String, Object>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 取得私鑰
*/
public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return en.encode(key.getEncoded());
}
/**
* 取得公鑰
*/
public static String getPublicKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return en.encode(key.getEncoded());
}
4.2.2 加密解密
/**
* 解密<br>
* 用私鑰解密
*/
public static String decrypt(byte[] data, String key) throws Exception {
// 對(duì)密鑰解密
byte[] keyBytes = de.decodeBuffer(key);
// 取得私鑰
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("EC", "SunEC");
ECPrivateKey priKey = (ECPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(priKey.getS(), priKey.getParams());
// 對(duì)數(shù)據(jù)解密
// Chipher不支持EC算法 未能實(shí)現(xiàn)
Cipher cipher = new NullCipher();
// Cipher.getInstance(ALGORITHM, keyFactory.getProvider());
cipher.init(Cipher.DECRYPT_MODE, priKey, ecPrivateKeySpec.getParams());
return new String(cipher.doFinal(data));
}
/**
* 加密<br>
* 用公鑰加密
*/
public static String encrypt(byte[] data, String privateKey) throws Exception {
// 對(duì)公鑰解密
byte[] keyBytes = de.decodeBuffer(privateKey);
// 取得公鑰
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("EC", "SunEC");
ECPublicKey pubKey = (ECPublicKey) keyFactory.generatePublic(x509KeySpec);
ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(pubKey.getW(), pubKey.getParams());
// 對(duì)數(shù)據(jù)加密
// TODO Chipher不支持EC算法 未能實(shí)現(xiàn)
Cipher cipher = new NullCipher();
// Cipher.getInstance(ALGORITHM, keyFactory.getProvider());
cipher.init(Cipher.ENCRYPT_MODE, pubKey, ecPublicKeySpec.getParams());
return en.encode(cipher.doFinal(data));
}
4.2.3 結(jié)果驗(yàn)證
public static void main(String[] args) throws Exception{
String inputStr = "abc";
byte[] data = inputStr.getBytes();
Map<String, Object> keyMap = initKey();
String publicKey = getPublicKey(keyMap);
String privateKey = getPrivateKey(keyMap);
System.err.println("公鑰: \r\n" + publicKey);
System.err.println("私鑰: \r\n" + privateKey);
String encodedData = encrypt(data, publicKey);
String decodedData = decrypt(de.decodeBuffer(encodedData), privateKey);
System.err.println("加密前: " + inputStr + "\n\n" + "解密后: " + decodedData);
}