RSA加密解密及數字簽名Java實現

denglt發表於2015-10-16

     RSA公鑰加密演算法是1977年由羅納德·李維斯特(Ron Rivest)、阿迪·薩莫爾(Adi Shamir)和倫納德·阿德曼(Leonard Adleman)一起提出的。當時他們三人都在麻省理工學院工作。RSA就是他們三人姓氏開頭字母拼在一起組成的。

    RSA是目前最有影響力的公鑰加密演算法,它能夠抵抗到目前為止已知的絕大多數密碼攻擊,已被ISO推薦為公鑰資料加密演算法

    RSA演算法是一種非對稱密碼演算法,所謂非對稱,就是指該演算法需要一對金鑰,使用其中一個加密,則需要用另一個才能解密。

    關於RSA演算法的原理,這裡就不再詳加介紹,網上各種資源一大堆。下面就開始介紹RSA加密解密JAVA類的具體實現。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import java.security.MessageDigest;
 
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
 
public class Coder {
     
    public static final String KEY_SHA="SHA";
    public static final String KEY_MD5="MD5";
     
    /**
     * BASE64解密
     * @param key
     * @return
     * @throws Exception
     */
    public static byte[] decryptBASE64(String key) throws Exception{
        return (new BASE64Decoder()).decodeBuffer(key);
    }
     
    /**
     * BASE64加密
     * @param key
     * @return
     * @throws Exception
     */
    public static String encryptBASE64(byte[] key)throws Exception{
        return (new BASE64Encoder()).encodeBuffer(key);
    }
     
    /**
     * MD5加密
     * @param data
     * @return
     * @throws Exception
     */
    public static byte[] encryptMD5(byte[] data)throws Exception{
        MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);
        md5.update(data);
        return md5.digest();
    }
     
    /**
     * SHA加密
     * @param data
     * @return
     * @throws Exception
     */
    public static byte[] encryptSHA(byte[] data)throws Exception{
        MessageDigest sha = MessageDigest.getInstance(KEY_SHA);
        sha.update(data);
        return sha.digest();
    }
}

     先提供Coder編碼類,該類封裝了基本的Base64、md5和SHA加密解密演算法。Java對這些演算法的實現提供了很好的API封裝,開發人員只需呼叫這些API就可很簡單方便的實現資料的加密與解密。

    下面提供RSA加密解密類,該類為Coder類子類,因為其中對RSA公私金鑰的儲存進行了一層Base64加密處理。

    RSA加密解密類靜態常量

?
1
2
3
4
5
   public static final String KEY_ALGORTHM="RSA";//
public static final String SIGNATURE_ALGORITHM="MD5withRSA";
 
public static final String PUBLIC_KEY = "RSAPublicKey";//公鑰
public static final String PRIVATE_KEY = "RSAPrivateKey";//私鑰


    RSA加密解密的實現,需要有一對公私金鑰,公私金鑰的初始化如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
     * 初始化金鑰
     * @return
     * @throws Exception
     */
    public static Map<String,Object> initKey()throws Exception{
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORTHM);
        keyPairGenerator.initialize(1024);
        KeyPair keyPair = keyPairGenerator.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;
    }

      從程式碼中可以看出金鑰的初始化長度為1024位,金鑰的長度越長,安全性就越好,但是加密解密所用的時間就會越多。而一次能加密的密文長度也與金鑰的長度成正比。一次能加密的密文長度為:金鑰的長度/8-11。所以1024bit長度的金鑰一次可以加密的密文為1024/8-11=117bit。所以非對稱加密一般都用於加密對稱加密演算法的金鑰,而不是直接加密內容。對於小檔案可以使用RSA加密,但加密過程仍可能會使用分段加密。

    從map中獲取公鑰、私鑰

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
     * 取得公鑰,並轉化為String型別
     * @param keyMap
     * @return
     * @throws Exception
     */
    public static String getPublicKey(Map<String, Object> keyMap)throws Exception{
        Key key = (Key) keyMap.get(PUBLIC_KEY); 
        return encryptBASE64(key.getEncoded());    
    }
 
    /**
     * 取得私鑰,並轉化為String型別
     * @param keyMap
     * @return
     * @throws Exception
     */
    public static String getPrivateKey(Map<String, Object> keyMap) throws Exception{
        Key key = (Key) keyMap.get(PRIVATE_KEY); 
        return encryptBASE64(key.getEncoded());    
    }

    對於RSA產生的公鑰、私鑰,我們可以有兩種方式可以對資訊進行加密解密。私鑰加密-公鑰解密 和 公鑰加密-私鑰解密。

    私鑰加密

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
     * 用私鑰加密
     * @param data  加密資料
     * @param key   金鑰
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPrivateKey(byte[] data,String key)throws Exception{
        //解密金鑰
        byte[] keyBytes = decryptBASE64(key);
        //取私鑰
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
        Key privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
         
        //對資料加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
         
        return cipher.doFinal(data);
    }

    私鑰解密

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
     * 用私鑰解密<span style="color:#000000;"></span> * @param data  加密資料
     * @param key   金鑰
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPrivateKey(byte[] data,String key)throws Exception{
        //對私鑰解密
        byte[] keyBytes = decryptBASE64(key);
         
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
        Key privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        //對資料解密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
         
        return cipher.doFinal(data);
    }
    公鑰加密
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
     * 用公鑰加密
     * @param data  加密資料
     * @param key   金鑰
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPublicKey(byte[] data,String key)throws Exception{
        //對公鑰解密
        byte[] keyBytes = decryptBASE64(key);
        //取公鑰
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
        Key publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
         
        //對資料解密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
         
        return cipher.doFinal(data);
    }
    私鑰加密
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
     * 用公鑰解密
     * @param data  加密資料
     * @param key   金鑰
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPublicKey(byte[] data,String key)throws Exception{
        //對私鑰解密
        byte[] keyBytes = decryptBASE64(key);
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
        Key publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
         
        //對資料解密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
         
        return cipher.doFinal(data);
    }

    關於數字簽名,先了解下何為數字簽名。數字簽名,就是隻有資訊的傳送者才能產生的別人無法偽造的一段數字串,這段數字串同時也是對資訊的傳送者傳送資訊真實性的一個有效證明。數字簽名是非對稱金鑰加密技術與數字摘要技術的應用。簡單地說,所謂數字簽名就是附加在資料單元上的一些資料,或是對資料單元所作的密碼變換。這種資料或變換允許資料單元的接收者用以確認資料單元的來源和資料單元的完整性並保護資料,防止被人(例如接收者)進行偽造。

    數字簽名的主要功能如下:

    保證資訊傳輸的完整性、傳送者的身份認證、防止交易中的抵賴發生。

    數字簽名技術是將摘要資訊用傳送者的私鑰加密,與原文一起傳送給接收者。接收者只有用傳送者的公鑰才能解密被加密的摘要資訊,然後用對收到的原文產生一個摘要資訊,與解密的摘要資訊對比。如果相同,則說明收到的資訊是完整的,在傳輸過程中沒有被修改,否則說明資訊被修改過,因此數字簽名能夠驗證資訊的完整性。

    數字簽名是個加密的過程,數字簽名驗證是個解密的過程。

     數字簽名演算法依靠公鑰加密技術來實現的。在公鑰加密技術裡,每一個使用者有一對金鑰:一把公鑰和一把私鑰。公鑰可以自由釋出,但私鑰則祕密儲存;還有一個要求就是要讓通過公鑰推算出私鑰的做法不可能實現。
    普通的數字簽名演算法包括三種演算法:
    1.密碼生成演算法;
    2.標記演算法;

   3.驗證演算法

    通過RSA加密解密演算法,我們可以實現數字簽名的功能。我們可以用私鑰對資訊生成數字簽名,再用公鑰來校驗數字簽名,當然也可以反過來公鑰簽名,私鑰校驗。

    私鑰簽名

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
     *  用私鑰對資訊生成數字簽名
     * @param data  //加密資料
     * @param privateKey    //私鑰
     * @return
     * @throws Exception
     */
    public static String sign(byte[] data,String privateKey)throws Exception{
        //解密私鑰
        byte[] keyBytes = decryptBASE64(privateKey);
        //構造PKCS8EncodedKeySpec物件
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
        //指定加密演算法
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
        //取私鑰匙物件
        PrivateKey privateKey2 = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        //用私鑰對資訊生成數字簽名
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(privateKey2);
        signature.update(data);
         
        return encryptBASE64(signature.sign());
    }
    公鑰校驗 


?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
     * 校驗數字簽名
     * @param data  加密資料
     * @param publicKey 公鑰
     * @param sign  數字簽名
     * @return
     * @throws Exception
     */
    public static boolean verify(byte[] data,String publicKey,String sign)throws Exception{
        //解密公鑰
        byte[] keyBytes = decryptBASE64(publicKey);
        //構造X509EncodedKeySpec物件
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
        //指定加密演算法
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
        //取公鑰匙物件
        PublicKey publicKey2 = keyFactory.generatePublic(x509EncodedKeySpec);
         
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(publicKey2);
        signature.update(data);
        //驗證簽名是否正常
        return signature.verify(decryptBASE64(sign));
         
    }
       對於RSA如何加密檔案、圖片等資訊,加密的資訊又如何儲存,怎樣儲存解密後的資訊,以及操作過程中遇到的錯誤將如何處理,將在後面的文章中介紹給大家。 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/195110/viewspace-1814611/,如需轉載,請註明出處,否則將追究法律責任。

相關文章