RSA算法在android下的应用

RSA 介绍


1. 历史

        RSA是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的。

    1973年,在英国政府通讯总部工作的数学家克利福德·柯克斯(Clifford Cocks)在一个内部文件中提出了一个相同的算法,但他的发现被列入机密,一直到1997年才被发表

2. 原理
加密解密
公式密文=明文 EmodN明文=密文DmodN
解释对明文的E次方后除以N后求余数的过程对密文进行D次方后除以N的余数就是明文
密钥E、N是RSA加密的密钥,也就是说E和N的组合就是公钥,公钥=(E,N) 。 E是加密(Encryption)的首字母,N是数字(Number)的首字母知道D和N就能进行解密密文了,所以D和N的组合就是私钥,私钥=(D,N)。 D是解密(Decryption)的首字母;N是数字(Number)的首字母

注:E和N不并不是随便什么数都可以的,它们都是经过严格的数学计算得

  • 生成密钥对
         既然公钥是(E,N),私钥是(D,N)所以密钥对即为(E,D,N)但密钥对是怎样生成的?
    步骤如下:
    求N ——> 求L(L为中间过程的中间数)—— >求E ——> 求D

    1. 求N
    准备两个质数p,q。这两个数不能太小,太小则会容易破解,将p乘以q就是N
    N=p∗q
    2. 求L
    L 是 p-1 和 q-1的最小公倍数,可用如下表达式表示
    L=lcm(p-1,q-1)L=lcm(p-1,q-1)
    3. 求E
    E必须满足两个条件:E是一个比1大比L小的数,E和L的最大公约数为1
    用gcd(X,Y)来表示X,Y的最大公约数则E条件如下:
    1 < E < L
    gcd(E,L)=1
    之所以需要E和L的最大公约数为1是为了保证一定存在解密时需要使用的数D。现在我 们已经求出了E和N也就是说我们已经生成了密钥对中的公钥了。
    4. 求D
    数D是由数E计算出来的。D、E和L之间必须满足以下关系:
    1 < D < L
    E*D mod L = 1
    只要D满足上述2个条件,则通过E和N进行加密的密文就可以用D和N进行解密。

3. 非对称

     非对称加密算法需要两个密钥:公开密钥(publickey:简称公钥)和私有密钥(privatekey:简称私钥)。公钥与私钥是一对,如果用公钥对数据进行加密,只有用对应的私钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。
     非对称加密算法实现机密信息交换的基本过程是:甲方生成一对密钥并将公钥公开,需要向甲方发送信息的其他角色(乙方)使用该密钥(甲方的公钥)对机密信息进行加密后再发送给甲方;甲方再用自己私钥对加密后的信息进行解密。甲方想要回复乙方时正好相反,使用乙方的公钥对数据进行加密,同理,乙方使用自己的私钥来进行解密。
     另一方面,甲方可以使用自己的私钥对机密信息进行签名后再发送给乙方;乙方再用甲方的公钥对甲方发送回来的数据进行验签。

4. 安全性

    对极大整数做因数分解的难度决定了RSA算法的可靠性。
    换言之,对一极大整数做因数分解愈困难,RSA算法愈可靠。假如有人找到一种快速因数分解的算法的话,那么用RSA加密的信息的可靠性就肯定会极度下降。但找到这样的算法的可能性是非常小的。今天只有短的RSA钥匙才可能被强力方式解破。
    到目前为止,世界上还没有任何可靠的攻击RSA算法的方式。只要其钥匙的长度足够长,用RSA加密的信息实际上是不能被解破的。

已公开的或已知的攻击方法

1,针对RSA最流行的攻击一般是基于大数因数分解。1999年,RSA-155 (512 bits)被成功分解,花了五个月时间(约8000 MIPS年)和224 CPU hours在一台有3.2G中央内存的Cray C916计算机上完成。

RSA-158表示如下:

39505874583265144526419767800614481996020776460304936454139376051579355626529450683609727842468219535093544305870490251995655335710209799226484977949442955603= 3388495837466721394368393204672181522815830368604993048084925840555281177×  11658823406671259903148376558383270818131012258146392600439520994131344334162924536139

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

RSA-768表示如下:

1230186684530117755130494958384962720772853569595334792197322452151726400507263657518745202199786469389956474942774063845925192557326303453731548268507917026122142913461670429214311602221240479274737794080665351419597459856902143413= 3347807169895689878604416984821269081770479498371376856891  2431388982883793878002287614711652531743087737814467999489×  3674604366679959042824463379962795263227915816434308764267  6032283815739666511279233373417143396810270092798736308917

2,秀尔算法
    量子计算里的秀尔算法能使穷举的效率大大的提高。由于RSA算法是基于大数分解(无法抵抗穷举攻击),因此在未来量子计算能对RSA算法构成较大的威胁。一个拥有N量子比特的量子计算机,每次可进行2^N次运算,
理论上讲,密钥为1024位长的RSA算法,用一台512量子比特位的量子计算机在1秒内即可破解。

opensll库中rsa接口应用


1. openssl 介绍

   OpenSSL是一个开放源代码的软件库包,应用程序可以使用这个包来进行安全通信,避免窃听,同时确认另一端连接者的身份。这个包广泛被应用在互联网的网页服务器上。
   OpenSSL 是一个安全套接字层密码库,囊括主要的密码算法、常用的密钥和证书封装管理功能及SSL协议,并提供丰富的应用程序供测试或其它目的使用。
   OpenSSL整个软件包大概可以分成三个主要的功能部分:SSL协议库、应用程序以及密码算法库。OpenSSL的目录结构自然也是围绕这三个功能部分进行规划的。

2. openssl移植

   去官网下载源码,配置编译。
   参考:https://blog.csdn.net/zhaoxd200808501/article/details/74331986

3. 几个重要概念
  • PEM : pem文件是openssl密钥使用的格式文件,它是将DER编码转码为base64得到的。
    例:
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDa+zB9rMw2dGQ1qeDh+gClqN1OXHYG9YgrT1x/vv8Oj+9X/q0I
Opo/Q9rwG/evc+3XDkwVDrqVUEByRjY7aLW/uZ+nreOgzt9gpqDL5w+b2DXzQtUB
sTE6x+jvBJap2igAh1jo+eXrVh5QJm+K7eCeMy3FY7nmzzD3rsbMVUPlrwIDAQAB
AoGARIfkDxCrAEfArMKzbEUvOqj0SXfKQDdLHDLEAf6mF8qX3vQG9tJQP4he1lIK
W9BuGKWSzexEHEJ3SMwMYN9OMVP8iRJscXXsFKa52xQ+iSgLo+3YOhwpg90ajZfQ
ZE0wTdp4U3m+i0iUW2EbHqr25BTdp5AnrEc+pG9lPx8gxKECQQD1lmdK/RVNb+Br
lc6sEYtZRlOUuWO9BKtt8kJnNCtHev3MciruM1I98gKjSucCohHD6mC/XGmfGOWY
n/8GrNl3AkEA5EQAkV0SoMub2ucE8WFNMxy1QUPu+vGe+Iat+7JgP2PnnDGu6r7k
ZSPKb52Q5U78hgIbwE8OA6XKw/nWHUnjiQJAQPHUQQVqg77pNyo3rFM7aZFqevMH
yC2a9AlTvB0UsON6iH1Mkw9pWU5Nmkctjgmz7v5lNVXH7LXVyaXN+ELvgwJBAKEa
VYRCuhj+Wvt+PQDXeZLvWgW8GnjF+zrQYw7XcBKNQjP85MUNAUlYn1FzVYZh3tv9
tPRfVza/1oHGXJXDRnECQEPY05ipbScf/qh+AGtp1BganN6hxBOaTmPLKKqS5zK7
N7Yq8OaNBJFPVXQjOG2kt0RA3+j08RnaRY9vH0u1398=
-----END RSA PRIVATE KEY-----
  • PKCS: RSA代表的是一种算法,PKCS 代表的这种算法的一系列标准。常用到的是PKCS#1、PKCS#8。
  • PKCS#1:
    PKCS #1 标准是专门为 RSA 密钥进行定义的,其对应的 PEM 文件格式如下,
-----BEGIN RSA PUBLIC KEY-----
BASE64 ENCODED DATA
-----END RSA PUBLIC KEY-----
  • PKCS#8:
    PKCS#8 标准定义了一个密钥格式的通用方案,它不仅仅为 RSA 所使用,同样也可以被其它密钥所使用;
    其所对应的 PEM 格式定义如下,
-----BEGIN PUBLIC KEY-----
BASE64 ENCODED DATA
-----END PUBLIC KEY-----

注意,这里就没有 RSA 字样了,因为 PKCS#8 是一个通用型的秘钥格式方案;其中的 BASE64 ENCODED DATA 所标注的内容为 PEM 格式中对 DER 原始二进制进行的 BASE64 编码;

  • BASE64: 将二进制码转码成ASICC码的一种转码方式。
  • DEM: 对ASN.1协议的具体实现方式之一。使用ASN.1语法将数据对象转换成二进制字节码
  • ASN.1: 一种协议

   为了解决高级语言中结构化数据在网络传输中的结构关系能送达目的地进行还原,出现了以下几种数据序列化的方法:ASN.1,XML,Json等。
   ASN.1本身只定义了表示信息的抽象句法,但是没有限定其编码的方法,它与语言实现和物理标识无关。各种ASN.1编码规则提供了由ASN.1描述其抽象句法的数据的值的传送语法(具体表达)。

4.原始密钥的ASN.1语法封装

   ASN.1语法是一种抽象语法,定义了数据的常用结构(包括不同的数据类型),并且建立了和应用层对话所用的构架。

在PKCS#1 RSA算法标准中定义RSA私钥语法为:

RSAPrivateKey ::= SEQUENCE {

version Version,

modulus INTEGER, -- n

publicExponent INTEGER, -- e

privateExponent INTEGER, -- d

prime1 INTEGER, -- p

prime2 INTEGER, -- q

exponent1 INTEGER, -- d mod (p-1)

exponent2 INTEGER, -- d mod (q-1)

coefficient INTEGER, -- (inverse of q) mod p

otherPrimeInfos OtherPrimeInfos OPTIONAL

}

类型RSAPrivateKey 的各域具有以下意义:

• version 是版本号,为了与本文档的今后版本兼容。本篇文档的这个版本号应该是0,如果使用了多素数,则版本号应该是1。

Version ::= INTEGER { two-prime(0), multi(1) }

(CONSTRAINED BY {-- version must be multi if otherPrimeInfos present --})

• modulus 是RSA合数模n。

• publicExponent 是RSA的公开幂e。

• privateExponent 是RSA的私有幂d。

• prime1 是n的素数因子p。

• prime2 i是n的素数因子q。

• exponent1 等于d mod (p − 1)。

• exponent2 等于d mod (q − 1)。

• coefficient 是CRT系数 q–1 mod p。

• otherPrimeInfos 按顺序包含了其它素数r3,, ru的信息。如果version是0 ,它应该被忽略;而如果version是1,它应该至少包含OtherPrimeInfo的一个实例。

OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo

OtherPrimeInfo ::= SEQUENCE {

prime INTEGER, -- ri

exponent INTEGER, -- di

coefficient INTEGER -- ti

}

OtherPrimeInfo的各域具有以下意义:

• prime 是n的一个素数因子ri ,其中i ≥ 3。

• exponent 是di = d mod (ri − 1)。

• coefficient 是CRT系数 ti = (r1 · r2 · … · ri–1)1 mod ri。

 

公钥语法为:

RSAPublicKey ::= SEQUENCE {

modulus INTEGER, -- n

publicExponent INTEGER -- e

}

类型RSAPublicKey的域具有以下意义:

• modulus 是RSA的合数模n。

• publicExponent 是RSA公开幂e。

pem 就是将以上数据结构用TLV格式组装成文件,存储起来。

5.在android下的编程
  • Android源码中自带了openssl库
    路径:/external/openssl

  • 调用
    头文件:
    在这里插入图片描述
    链接库:
    链接库

  • API:

  1. 去读密钥文件:
    公钥:PEM_read_RSAPublicKey ——针对pcks#1
          PEM_read_RSA_PUBKEY ——针对pcks#8
    私钥:PEM_read_RSAPrivateKey 不区分PEM编码格式
 FILE *fp = NULL;
 RSA *Rsa
 
    if ((fp = fopen(patch, "r")) == NULL) {
        SLOGE("open %s error", patch);
        return -1;
    }
    //返回一个RSA对象
    if ((Rsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL)) == NULL) {
            SLOGE("rsa read public.pem error");
            get_openssl_err();
            fclose(fp);
            return -1;
        }

  1. 公钥加密,私钥解密
    RSA_public_encrypt
    RSA_private_decrypt
//公钥加密
//**参数:
//**1.明文长度
//**2.明文
//**3.输出
//**4.填充方式
//** return 密文长度 (是一个固定值 == 密钥长度)
 if (RSA_public_encrypt(inLen > pdBlock?pdBlock:inLen, (unsigned char *)in, (unsigned char*)rsa_out, rsa, RSA_PAD_MODE) < 0)
        {
            SLOGE("RSA encrypt error");
            get_openssl_err();
            RSA_free(rsa);
            return -1;
        }

明文长度填充方式的关系:

1.RSA_PKCS1_PADDING 填充模式,最常用的模式
一次加密的明文长度,不得大于 密钥长度 - 11。例如1024bit(128btye)的密钥,一次自能加密117个字节。对于长数据,需要分组加密。

2.RSA_PKCS1_OAEP_PADDING
一次加密的明文长度,不得大于 密钥长度 - 41

3.for RSA_NO_PADDING  不填充
一次加密的明文长度可以和 密钥一样长

//获取密钥长度
RSA_size(rsa)
//私钥解密
//参数:
//1、密文长度 (固定值 == 密钥长度)
//2、密文 
//3、明文
//4、填充模式
     if ((ret = RSA_private_decrypt(pdBlock, (unsigned char *)data, (unsigned char*)result, rsa, RSA_PAD_MODE)) < 0) {
            printf("RSA decrypt error!");
            get_openssl_err();
            RSA_free(rsa);
            return -1;
        }
  1. 私钥签名,公钥 验证
    RSA_private_encrypt
    RSA_public_decrypt
//私钥签名
//参数:
//1.明文长度
//2.明文
//3.输出的密文
//4.填充
   if (RSA_private_encrypt(inLen > pdBlock?pdBlock:inLen, (unsigned char *)in, (unsigned char*)rsa_out, rsa, RSA_PAD_MODE) < 0) {
            printf("RSA encrypt error");
            RSA_free(rsa);
            return -1;
        }

//公钥校验
     if ((ret = RSA_public_decrypt(pdBlock, (unsigned char *)data, (unsigned char*)result, rsa, RSA_PAD_MODE)) < 0)
        {
            SLOGE("RSA decrypt error!");
            get_openssl_err();
            RSA_free(rsa);
            return -1;
        }

  1. openssl debug
//输出错误码的字符串描述 
void get_openssl_err()
{
    ERR_load_ERR_strings();
    ERR_load_crypto_strings();
    unsigned long ulErr = ERR_get_error();
    char szErrMsg[1024] = {0};
    char *pTmp = NULL;

    pTmp = ERR_error_string(ulErr,szErrMsg); // 格式:error:errId:库:函数:原因
    SLOGE("error = %s",pTmp);
}

5、新版本上的接口

//新签名加密方案,老版本可能不支持,只能用上边通用方案

OPENSSL_EXPORT int RSA_sign(int hash_nid, const uint8_t *in,
                            unsigned int in_len, uint8_t *out,
                            unsigned int *out_len, RSA *rsa);
OPENSSL_EXPORT int RSA_verify(int hash_nid, const uint8_t *msg, size_t msg_len,
                              const uint8_t *sig, size_t sig_len, RSA *rsa);
OPENSSL_EXPORT int RSA_encrypt(RSA *rsa, size_t *out_len, uint8_t *out,
                               size_t max_out, const uint8_t *in, size_t in_len,
                               int padding);
OPENSSL_EXPORT int RSA_decrypt(RSA *rsa, size_t *out_len, uint8_t *out,
                               size_t max_out, const uint8_t *in, size_t in_len,
                               int padding);

6. 终端命令生成密钥

第一步:生成私钥,这里我们指定私钥的长度为2048

openssl genrsa -out rsa_private_key.pem 2048

第二步:根据私钥生成对应的公钥:

openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key_2048.pub

第三步:私钥转化成pkcs8格式,【这一步非必须,只是程序解析起来方便】,尖括号的意思是:将转化好的私钥写到rsa_private_key_pkcs8.pem文件里

openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt > rsa_private_key_pkcs8.pem

附:在线生成秘钥对工具 http://web.chacuo.net/netrsakeypair

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值