Android加密算法之非对称加密RSA

来源:互联网 发布:女程序员会嫁什么人 编辑:程序博客网 时间:2024/05/16 17:59

版权说明 :《Android加密算法之非对称加密RSA》于当前CSDN博客乘月网属同一原创,转载请说明出处,谢谢。



       上文《Android加密算法之对称加密AES》提到与对称加密算法相对应的非对称加密RSA,本文正式进入该算法的简单讲解和使用。

       非对称加密算法:加密和解密使用不同密钥的加密算法,也称为公私钥加密。不同的秘钥指作公开密钥(publickey)和私有秘钥(privatekey),两个天生一对,密不可分。如果用公钥对数据进行加密,只有用对应的私钥才能解密,如果用私钥对数据进行加密,那么只有用对应的公钥才能解密。

       上述可能会给小伙伴们一个很友好的疑问,都可以互相加解密,为什么有公私之分,随便给两个密钥公私身份可以吗?

       首先从长度上来比较,私钥长度明显比公钥高不少,这意味着对密钥的记忆难度有着很大差别,另外在逆向破解中,由于该算法特性,长度短的公钥想逆向推导出私钥的代价是相当昂贵的,相反私钥推导出公钥相对来说简单到爆炸,因此我们会本能的想到,长度短的密钥公开给众人使用解开自己的公布的密文,而长度长的密钥由自己私有保管。

       其次从非对称加密的设计本意来说:公钥加密,私钥解密;私钥加签,公钥验签。设想A和B两位是好友,A持有公钥PK-A和私钥Sk-A,B持有公钥PK-B和私钥SK-B,PK-A和PK-B都是公开的,全世界人都知道,而SK-A和SK-B分别只有A和B自己知道。分析以下场景:

       场景1:A给B发信息,在通信过程中,信息内容可能被非法之徒劫持,如果A没有作加密处理,那就.....后果可想而知,那么A会选择用什么密钥加密?我们逐一排除,选择PK-A?加密完后,只有SK-A解密,这倒好了,劫持者不知道SK-A,无法解密就算了,B也不知道啊,也解不了,PASS掉!选SK-A?OK,那么只能PK-A解密,而B和劫持者都知道PK-A,都能解密,这跟没加密有什么区别?狠狠的PASS掉!选SK-B?你想多了,只有B知道SK-B啊,没得选,只能选PK-B,那么只能用SK-B解密了,劫持者并不知道SK-B,只能干看着密文着急,而B能轻松解密并给查看A给自己发的内容了。

       场景2:A对B突发好感,给其写了一封匿名电子情书,B看了后很感动但是以为是C写的,于是向作为好友的A分享自己的喜悦,这时候A着急了,说是自己写的,B就是不相信,以为在逗他。没办法,A只能出大招,高度自信地告诉B把这封信上的一处签名解开就是A的名字,那么问题来了,A用什么密钥来制作签名的?为啥这么自信?PK-B?,全世界人都可以制作这个签名,挂掉!SK-B?没人知道啊,怎么制作啊?毙掉!PK-A?别人也知道啊,也可以制作这个签名啊,那么只能SK-A了,只有A能做这样的签名,这时候B拿A的PK-A验证这个签名,验证的结果正是A的姓名。从此二人过上了愉快的基友生活.....咦。。。。。。。。

相信上面两个场景,会让小伙伴们感悟颇深,无法忘怀,理解为重,理解为重哈。。。


     回到主题,之前文章《Android加密算法之对称加密AES》我们知道,对称加密算法具有算法公开、计算量小、加密速度快、加密效率高等优点,非对称加密算法则比之慢数千倍,这一点笔者在做RSA加解密的时候深有感触,这里提醒Android Developer在做非对称加密的时候最好能在子线程中执行,否则容易造成ANR。话说回来,非对称加密算法在保护通信安全方面,具有对称加密难以企及的优势,具体以后文章会更新拓展。

         非对称加密主要使用的是RSA算法,Android RSA算法一样沿用java的API,公私钥长度建议为2048位(至少1024及以上,以下已被破解),推荐"RSA/ECB/OAEPWithSHA256AndMGF1Padding"填充方式,否则容易被重放攻击。下面为RSA实现工具类RSAUtils.java :

package cn.icheny.security;import java.io.UnsupportedEncodingException;import java.security.InvalidKeyException;import java.security.KeyFactory;import java.security.KeyPair;import java.security.KeyPairGenerator;import java.security.NoSuchAlgorithmException;import java.security.PrivateKey;import java.security.PublicKey;import java.security.interfaces.RSAPrivateKey;import java.security.interfaces.RSAPublicKey;import java.security.spec.InvalidKeySpecException;import java.security.spec.PKCS8EncodedKeySpec;import java.security.spec.X509EncodedKeySpec;import javax.crypto.BadPaddingException;import javax.crypto.Cipher;import javax.crypto.IllegalBlockSizeException;import javax.crypto.NoSuchPaddingException;import android.util.Base64;/** * RSA加密,解密工具 *  * @author Cheny */public class RSAUtils {// 推荐的RSA加密模式,否则容易被重放攻击private static final String RSA_ECB_OAEP_WITH_SHA256_AND_MGF1_PADDING = "RSA/ECB/OAEPWithSHA256AndMGF1Padding";private static final String RSA = "RSA";/** * 生成秘钥,公钥和私钥 *  * @return Base64编码的公钥和私钥 secretKeys[]{publicKey,privateKey} */public static String[] generateSecretKey() {String[] secretKeys = null;try {// 构建密钥对生成器KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA);// 初始化秘钥长度 2048位(512位已被破解,至少用1024及以上)keyPairGenerator.initialize(2048);// 生成密钥对,包含公钥私钥KeyPair keyPair = keyPairGenerator.generateKeyPair();// 公钥对象,RSAPublicKey为PublicKey子接口RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();// 公钥对象,RSAPrivateKey为PrivateKey子接口RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();// 公钥byte[] publicKeyBytes = rsaPublicKey.getEncoded();// 私钥byte[] privateKeyBytes = rsaPrivateKey.getEncoded();secretKeys = new String[2];// 对公私钥Base64转码,加解密时需要把转码后的公私钥进行Base64还原secretKeys[0] = Base64.encodeToString(publicKeyBytes, Base64.DEFAULT);secretKeys[1] = Base64.encodeToString(privateKeyBytes, Base64.DEFAULT);} catch (NoSuchAlgorithmException e) {e.printStackTrace();}return secretKeys;}/** * 公钥加密 *  * @param plaintext 明文 * @param publicKey 公钥 * @return Base64编码的密文 */public static String encryptByPublicKey(String plaintext, String rsaPublicKey) {try {// Base64还原公钥byte[] publicKeyBytes = Base64.decode(rsaPublicKey, Base64.DEFAULT);// X509编码秘钥规范X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKeyBytes);// 秘钥工厂KeyFactory keyFactory = KeyFactory.getInstance(RSA);// 还原公钥对象,PublicKey为RSAPublicKey父接口PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);// Cipher对象,指定算法模式Cipher cipher = Cipher.getInstance(RSA_ECB_OAEP_WITH_SHA256_AND_MGF1_PADDING);// 初始化,指定为加密模式cipher.init(Cipher.ENCRYPT_MODE, publicKey);// 执行加密byte[] result = cipher.doFinal(plaintext.getBytes("UTF-8"));return Base64.encodeToString(result, Base64.DEFAULT); // 对密文Base64转码,解密时需要把转码后的密文进行Base64还原} catch (InvalidKeyException e) {e.printStackTrace();} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (InvalidKeySpecException e) {e.printStackTrace();} catch (NoSuchPaddingException e) {e.printStackTrace();} catch (IllegalBlockSizeException e) {e.printStackTrace();} catch (BadPaddingException e) {e.printStackTrace();} catch (UnsupportedEncodingException e) {e.printStackTrace();}return null;}/** * 公钥解密 *  * @param plaintext 明文 * @param publicKey 公钥 * @return Base64编码的密文 */public static String decryptByPublicKey(String ciphertext, String rsaPublicKey) {try {// Base64还原公钥byte[] publicKeyBytes = Base64.decode(rsaPublicKey, Base64.DEFAULT);// X509秘钥编码规范X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKeyBytes);// 秘钥工厂KeyFactory keyFactory = KeyFactory.getInstance(RSA);// 还原公钥对象,PublicKey为RSAPublicKey父接口PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);// Cipher对象,指定算法模式Cipher cipher = Cipher.getInstance(RSA_ECB_OAEP_WITH_SHA256_AND_MGF1_PADDING);// 初始化,指定为解密模式cipher.init(Cipher.DECRYPT_MODE, publicKey);// Base64还原密文byte[] cipherBytes = Base64.decode(ciphertext, Base64.DEFAULT);// 执行解密byte[] result = cipher.doFinal(cipherBytes);return new String(result, "UTF-8");} catch (InvalidKeyException e) {e.printStackTrace();} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (InvalidKeySpecException e) {e.printStackTrace();} catch (NoSuchPaddingException e) {e.printStackTrace();} catch (IllegalBlockSizeException e) {e.printStackTrace();} catch (BadPaddingException e) {e.printStackTrace();} catch (UnsupportedEncodingException e) {e.printStackTrace();}return null;}/** * 私钥加密 *  * @param plaintext  明文 * @param rsaPrivateKey 私钥 * @return Base64编码的密文 */public static String encryptByPrivateKey(String plaintext, String rsaPrivateKey) {try {// Base64还原私钥byte[] privateKeyBytes = Base64.decode(rsaPrivateKey, Base64.DEFAULT);// PKCS8秘钥编码规范PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);// 秘钥工厂KeyFactory keyFactory = KeyFactory.getInstance(RSA);// 还原公钥对象,PrivateKey为RSAPrivateKey父接口PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);// Cipher对象,指定算法模式Cipher cipher = Cipher.getInstance(RSA_ECB_OAEP_WITH_SHA256_AND_MGF1_PADDING);// 初始化,指定为加密模式cipher.init(Cipher.ENCRYPT_MODE, privateKey);// 执行加密byte[] result = cipher.doFinal(plaintext.getBytes("UTF-8"));return Base64.encodeToString(result, Base64.DEFAULT);// 对密文Base64转码,解密时需要把转码后的密文进行Base64还原} catch (InvalidKeyException e) {e.printStackTrace();} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (InvalidKeySpecException e) {e.printStackTrace();} catch (NoSuchPaddingException e) {e.printStackTrace();} catch (IllegalBlockSizeException e) {e.printStackTrace();} catch (BadPaddingException e) {e.printStackTrace();} catch (UnsupportedEncodingException e) {e.printStackTrace();}return null;}/** * 私钥解密 *  * @param ciphertext  密文 * @param key 秘钥 * @return 明文 */public static String decryptByPrivateKey(String ciphertext, String rsaPrivateKey) {try {// Base64还原私钥byte[] privateKeyBytes = Base64.decode(rsaPrivateKey, Base64.DEFAULT);// PKCS8秘钥编码规范PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);// 秘钥工厂KeyFactory keyFactory = KeyFactory.getInstance(RSA);// 还原公钥对象,PrivateKey为RSAPrivateKey父接口PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);// Cipher对象,指定算法模式Cipher cipher = Cipher.getInstance(RSA_ECB_OAEP_WITH_SHA256_AND_MGF1_PADDING);// 初始化,指定为解密模式cipher.init(Cipher.DECRYPT_MODE, privateKey);// Base64还原密文byte[] cipherBytes = Base64.decode(ciphertext, Base64.DEFAULT);// 执行解密byte[] result = cipher.doFinal(cipherBytes);return new String(result, "UTF-8");} catch (InvalidKeyException e) {e.printStackTrace();} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (InvalidKeySpecException e) {e.printStackTrace();} catch (NoSuchPaddingException e) {e.printStackTrace();} catch (IllegalBlockSizeException e) {e.printStackTrace();} catch (BadPaddingException e) {e.printStackTrace();} catch (UnsupportedEncodingException e) {e.printStackTrace();}return null;}}
          个人习惯,代码中注释得很详细,所以就不在此话似唐僧了,与其一边看代码,一边看代码下文的解释让人视觉疲劳,还不如把注释写好,笔者深有体会。文章先到这,日后会继续更新。。。
0 0
原创粉丝点击