RSA那點事

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

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

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

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


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加密解密類靜態常量

       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加密解密的實現,需要有一對公私密鑰,公私密鑰的初始化如下:

/**
* 初始化密鑰
* @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中獲取公鑰、私鑰


/**
* 取得公鑰,並轉化爲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產生的公鑰、私鑰,我們可以有兩種方式可以對信息進行加密解密。私鑰加密-公鑰解密 和 公鑰加密-私鑰解密。
 


    私鑰加密
/**
* 用私鑰加密
* @param data 加密數據
* @param key 密鑰
* @return
* @throws Exception
*/
public static byte[] encryptByPrivateKey(byte[] data,String key)throws Exception{
Security.addProvider(new BouncyCastleProvider());
//解密密鑰
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);

}


    私鑰解密
/**
* 用私鑰解密 * @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);

}


    公鑰加密
/**
* 用公鑰加密
* @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);

}


    私鑰加密

/**
* 用公鑰解密
* @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加密解密算法,我們可以實現數字簽名的功能。我們可以用私鑰對信息生成數字簽名,再用公鑰來校驗數字簽名,當然也可以反過來公鑰簽名,私鑰校驗。

    

私鑰簽名


/**
* 用私鑰對信息生成數字簽名
* @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());

}


    公鑰校驗 

/**
* 校驗數字簽名
* @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));

}

發佈了20 篇原創文章 · 獲贊 9 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章