http://www.cnblogs.com/AloneSword/p/3326750.html
RSA是目前最有影響力的公鑰加密算法,該算法基於一個十分簡單的數論事實:將兩個大素數相乘十分容易,但那時想要對其乘積進行因式分解卻極其困難,因此可以將乘積公開作為加密密鑰,即公鑰,而兩個大素數組合成私鑰。公鑰是可發布的供任何人使用,私鑰則為自己所有,供解密之用。
解密者擁有私鑰,並且將由私鑰計算生成的公鑰發布給加密者。加密都使用公鑰進行加密,並將密文發送到解密者,解密者用私鑰解密將密文解碼為明文。
以甲要把信息發給乙為例,首先確定角色:甲為加密者,乙為解密者。首先由乙隨機確定一個KEY,稱之為密匙,將這個KEY始終保存在機器B中而不發出來;然后,由這個 KEY計算出另一個KEY,稱之為公匙。這個公鑰的特性是幾乎不可能通過它自身計算出生成它的私鑰。接下來通過網絡把這個公鑰傳給甲,甲收到公鑰后,利用公鑰對信息加密,並把密文通過網絡發送到乙,最后乙利用已知的私鑰,就對密文進行解碼了。以上就是RSA算法的工作流程。
算法實現過程為:
1. 隨意選擇兩個大的質數p和q,p不等於q,計算N=pq。
2. 根據歐拉函數,不大於N且與N互質的整數個數為(p-1)(q-1)。
3. 選擇一個整數e與(p-1)(q-1)互質,並且e小於(p-1)(q-1)。
4. 用以下這個公式計算d:d× e ≡ 1 (mod (p-1)(q-1))。
5. 將p和q的記錄銷毀。
以上內容中,(N,e)是公鑰,(N,d)是私鑰。
下面講解RSA算法的應用。
RSA的公鑰和私鑰是由KeyPairGenerator生成的,獲取KeyPairGenerator的實例后還需要設置其密鑰位數。設置密鑰位數越高,加密過程越安全,一般使用1024位。如下代碼:
[代碼]java代碼:
1 |
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(RSA); |
3 |
keyPairGen.initialize( 1024 ); |
公鑰和私鑰可以通過KeyPairGenerator執行generateKeyPair()后生成密鑰對KeyPair,通過KeyPair.getPublic()和KeyPair.getPrivate()來獲取。如下代碼:
[代碼]java代碼:
2 |
KeyPair keyPair = keyPairGen.generateKeyPair(); |
4 |
PublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); |
6 |
PrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); |
7 |
byte [] publicKeyData = publicKey.getEncoded(); |
8 |
byte [] privateKeyData = publicKey.getEncoded(); |
公鑰和私鑰都有它們自己獨特的比特編碼,可以通過getEncoded()方法獲取,返回類型為byte[]。通過byte[]可以再度將公鑰或私鑰還原出來。具體代碼如下:
[代碼]java代碼:
02 |
public static PublicKey getPublicKey( byte [] keyBytes) throws |
03 |
NoSuchAlgorithmException,InvalidKeySpecException { |
04 |
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); |
05 |
KeyFactory keyFactory = KeyFactory.getInstance( "RSA" ); |
06 |
PublicKey publicKey = keyFactory.generatePublic(keySpec); |
10 |
public static PrivateKey getPrivateKey( byte [] keyBytes) throws |
11 |
NoSuchAlgorithmException,InvalidKeySpecException { |
12 |
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); |
13 |
KeyFactory keyFactory = KeyFactory.getInstance( "RSA" ); |
14 |
PrivateKey privateKey = keyFactory.generatePrivate(keySpec); |
在上文講到的RSA算法實現過程中提到(N,e)是公鑰,(N,d)是私鑰。既然已經獲取到了PublicKey和PrivateKey了,那如何取到N、e、d這三個值呢。要取到這三個值,首先要將PublicKey和PrivateKey強制轉換成RSAPublicKey和RSAPrivateKey。共同的N值可以通過getModulus()獲取。執行RSAPublicKey.getPublicExponent()可以獲取到公鑰中的e值,執行RSAPrivateKey.getPrivateExponent()可以獲取私鑰中的d值。這三者返回類型都是BigInteger。代碼如下:
[代碼]java代碼:
02 |
public static void printPublicKeyInfo(PublicKey key){ |
03 |
RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey; |
04 |
Log.d(MainActivity.TAG, "RSAPublicKey:" ); |
05 |
Log.d(MainActivity.TAG, "Modulus.length=" + |
06 |
rsaPublicKey.getModulus().bitLength()); |
07 |
Log.d(MainActivity.TAG, "Modulus=" + |
08 |
rsaPublicKey.getModulus().toString()); |
09 |
Log.d(MainActivity.TAG, "PublicExponent.length=" + |
10 |
rsaPublicKey.getPublicExponent().bitLength()); |
11 |
Log.d(MainActivity.TAG, "PublicExponent=" + |
12 |
rsaPublicKey.getPublicExponent().toString()); |
16 |
public static void printPublicKeyInfo(PrivateKey key){ |
17 |
RSAPrivateKey rsaPublicKey = (RSAPrivateKey) privateKey; |
18 |
Log.d(MainActivity.TAG, "RSAPrivateKey:" ); |
19 |
Log.d(MainActivity.TAG, "Modulus.length=" + |
20 |
rsaPrivateKey.getModulus().bitLength()); |
21 |
Log.d(MainActivity.TAG, "Modulus=" + |
22 |
rsaPrivateKey.getModulus().toString()); |
23 |
Log.d(MainActivity.TAG, "PublicExponent.length=" + |
24 |
rsaPrivateKey.getPrivateExponent().bitLength()); |
25 |
Log.d(MainActivity.TAG, "PublicExponent=" + |
26 |
rsaPrivateKey.getPrivateExponent().toString()); |
由於程序中動態生成KeyPair對明文加密后生成的密文是不可測的,所以在實際開發中通常在生成一個KeyPair后將公鑰和私鑰的N、e、d這三個特征值記錄下來,在真實的開發中使用這三個特征值再去將PublicKey和PrivateKey還原出來。還原方法如下:
[代碼]java代碼:
02 |
public static PublicKey getPublicKey(String modulus, String |
04 |
throws NoSuchAlgorithmException, InvalidKeySpecException { |
05 |
BigInteger bigIntModulus = new BigInteger(modulus); |
06 |
BigInteger bigIntPrivateExponent = new BigInteger(publicExponent); |
07 |
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(bigIntModulus, |
08 |
bigIntPrivateExponent); |
09 |
KeyFactory keyFactory = KeyFactory.getInstance( "RSA" ); |
10 |
PublicKey publicKey = keyFactory.generatePublic(keySpec); |
15 |
public static PrivateKey getPrivateKey(String modulus, String |
17 |
throws NoSuchAlgorithmException, InvalidKeySpecException { |
18 |
BigInteger bigIntModulus = new BigInteger(modulus); |
19 |
BigInteger bigIntPrivateExponent = new BigInteger(privateExponent); |
20 |
RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(bigIntModulus, |
21 |
bigIntPrivateExponent); |
22 |
KeyFactory keyFactory = KeyFactory.getInstance( "RSA" ); |
23 |
PrivateKey privateKey = keyFactory.generatePrivate(keySpec); |
公鑰和私鑰都具備后,就可以使用加解密的工具類javax.crypto.Cipher對明文和密文進行處理了。與所有的引擎類一樣,可以通過調用Cipher類中的getInstance(String transformation)靜態工廠方法得到Cipher對象。該方法中的參數描述了由指定輸入產生輸出所進行的操作或操作集合,可以是下列兩種形式之一:“algorithm/mode/padding”或“algorithm”。例如下面的例子就是有效的transformation形式:"DES/CBC/PKCS5Padding"或"DES"。如果沒有指定模式或填充方式,就使用特定提供者指定的默認模式或默認填充方式。
Cipher的加密和解密方式所調用的方法和過程都一樣,只是傳參不同的區別。如下代碼:
[代碼]java代碼:
2 |
cipher.init(mode, key); |
4 |
byte [] dataResult = cipher.doFinal(input); |
Cipher.init(mode, key)方法中MODE指加密或解密模式,值為Cipher.ENCRYPT_MODE或Cipher.DECRYPT_MODE,參數key在加密時傳入PublicKey,在解密時以PrivateKey傳入。Cipher.doFinal(byte[] data)則是將待編碼數據傳入后並返回編碼結果。為了將編碼結果轉為可讀字符串,通常最后還使用BASE 64算法對最終的byte[]數據編碼后顯示給開發者。
Demo運行效果如下圖所示:
圖17-4 使用動態生成的公鑰和私鑰進行RSA加密
圖17-5 使用預設的N、e、d值進行RSA加密