Android中常用的加密算法——RSA加密

在上一遍Android中常用的加密算法——AES加密中我们介绍了对称加密和非对称加密,对称加密由于加密和解密使用同一个秘钥因此安全性与非对称加密相比要低得多。这一篇我们就来介绍一种被广泛应用的非对称加密——RSA加密。

RSA加密算法

RSA是一种应用十分广泛的非对称加密算法,在公开密钥加密和电子商业中RSA被广泛使用。RSA是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的。1973年,在英国政府通讯总部工作的数学家克利福德·柯克斯(Clifford Cocks)在一个内部文件中提出了一个相同的算法,但他的发现被列入机密,一直到1997年才被发表。——维基百科

其算法原理基于很简单的数学知识:既对两个大素数相乘得到其乘积很简单,但对乘积进行因数分解很难,两个大素数组合即为公钥,乘积未秘钥。只要保证两个不想等的素数足够大就可以保证加密足够安全。

算法实现

生成秘钥

2009年12月12日,编号为RSA-768(768 bits, 232 digits)数也被成功分解[10]。这一事件威胁了现通行的1024-bit密钥的安全性,普遍认为用户应尽快升级到2048-bit或以上。——维基百科

Android同样提供了系统类KeyPairGenerator用于辅助生成秘钥。

/**
 * 生产秘钥 秘钥长度建议不要小于1024
 * @param keyLength 秘钥长度,范围512 —— 2048
 * @return the secretKey
 * @throws Exception
 */
public static KeyPair generateKey(int keyLength) throws NoSuchAlgorithmException {
    // 获取秘钥生成器
    KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("RSA");
    keyGenerator.initialize(keyLength);
    // 生成秘钥并返回
    return keyGenerator.genKeyPair();
}

/**
 * 生产秘钥, 默认秘钥长度1024
 *
 * @return the secretKey
 * @throws Exception
 */
public static KeyPair generateKey() throws NoSuchAlgorithmException {
    return generateKey(1024);
}

上面的方法可以很便捷的在Android上生成秘钥,但实际开发中因为安全性问题很少在Android端生成秘钥(不排除特殊需求),通常是服务器端将公钥发送给Android端,Android端用公钥进行加密后发送给服务器端,服务器用私钥进行解密。秘钥的保存和传输通常会转换成一串ASCII码如下:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxlSpJwBEM/ia2P5jLTAGSxMexRxSKlmF
gIrPX7g0DsPIrqhMZMleTSLXOMT8D+1+T1UlHfwsJOybpVoliLoXIPIpk62TrJwDMPoXAZXP8AHS
vdcrDawQsypT3ZomUWup9yUoXUhcECqkyOeumfSKSCd1465UzWYJU3rKop/NTPWyELjBPZPRM+7V
X8ymmNLnTu01sCirM8KVVAkYmtZ1S15MnQaMCEyNOJGYHc0ctLcMKGfcesG/o+eapddJ0lvIwxWq
s6GsBTOui1e172SqdLqib8Zw/41WeVCXXIlj/O+b78k4gLM3tZBoAx9jOChRPZXW8cy/5Ug2HCS2
S+wNMwIDAQAB
-----END PUBLIC KEY-----

第一行和最后一行是标注秘钥的开始和结束,在转换为私钥和公钥对象是需要去掉。

//秘钥字符串生成Key需要通过Base64转换为byte[]
publicKey = RSAUtil.getPublicKey(Base64.decode(publicKeyStr, Base64.DEFAULT));
privateKey = RSAUtil.getPrivateKey(Base64.decode(privateKeyStr, Base64.DEFAULT));

转换为秘钥对象是需要先使用Base64类对秘钥字符串进行解码decode,同理如果要将Android端生成的秘钥进行保存或者传输通常也需要用Base64类进行编码encode转换为字符串。

/**
 *  将公钥的byte[]数据还原为PublicKey
 * @param publicKeyBytes
 * @return PublicKey
 * @throws Exception
 */
public static PublicKey getPublicKey(byte[] publicKeyBytes) throws Exception{
    X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    return keyFactory.generatePublic(keySpec);
}

/**
 * 将私钥的byte[]数据还原为PrivateKey
 * @param privateKey
 * @return PrivateKey
 * @throws Exception
 */
public static PrivateKey getPrivateKey(byte[] privateKey) throws Exception{
    PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    return keyFactory.generatePrivate(keySpec);
}

注意私钥转换和公钥转换用的类是不同的。

加密

/**
 * 用公钥加密,默认填充模式 RSA/ECB/PKCS1Padding
 * <br>每次加密的字节数,不能超过密钥的长度值减去11
 *
 * @param data  需加密数据的byte数据
 * @param publicKey 公钥
 * @return 加密后的byte型数据
 */
public static byte[] encrypt(byte[] data, PublicKey publicKey) throws Exception {
    return encrypt(data, publicKey, "RSA/ECB/PKCS1Padding");
}

/**
 * 用公钥加密
 * <br>每次加密的字节数,不能超过密钥的长度值减去11
 *
 * @param data  需加密数据的byte数据
 * @param publicKey 公钥
 * @param transformation 加密模式和填充方式
 * @return 加密后的byte型数据
 */
public static byte[] encrypt(byte[] data, PublicKey publicKey, String transformation) throws Exception {
    Cipher cipher = Cipher.getInstance(transformation);
    // 编码前设定编码方式及密钥
    cipher.init(Cipher.ENCRYPT_MODE, publicKey);
    // 传入编码数据并返回编码结果
    return cipher.doFinal(data);
}

加密模式和填充方式同样不过多探讨了参考上一篇提到的几篇文章,只要保存加密和解密端保持模式一致就好。

需要注意的是:加密时只能一次性加密秘钥长度减11字节的数据,假设秘钥长度为1024(下同),那么每次只能加密117字节(1024 / 8 - 11),若数据过长请分段加密。
有分段加密同样的也需要分段解密,前面我们说了每次只能加密117字节的数据,而这些数据加密之后的长度正好是秘钥长度1024bit,所以我们在解密时需要对每1024bit数据进行解密。

解密

/**
 * 用私钥解密,默认加密模式和填充方式为 RSA/ECB/PKCS1Padding"
 *
 * @param encryptedData 经过encrypt()加密返回的byte数据
 * @param privateKey私钥
 * @return
 */
public static byte[] decrypt(byte[] encryptedData, PrivateKey privateKey) throws Exception {
    return decrypt(encryptedData, privateKey,"RSA/ECB/PKCS1Padding");
}

/**
 * 用私钥解密
 *
 * @param encryptedData 经过encrypt()加密返回的byte数据
 * @param privateKey私钥
 * @param transformation 加密模式和填充方式
 * @return
 */
public static byte[] decrypt(byte[] encryptedData, PrivateKey privateKey, String transformation) throws Exception {
    Cipher cipher = Cipher.getInstance(transformation);
    cipher.init(Cipher.DECRYPT_MODE, privateKey);
    return cipher.doFinal(encryptedData);
}

注意加密模式和填充方式必须与加密时保持一致。

参考文章:

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,064评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,606评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,011评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,550评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,465评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,919评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,428评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,075评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,208评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,185评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,191评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,914评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,482评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,585评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,825评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,194评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,703评论 2 339

推荐阅读更多精彩内容