RSA 之padding

 

参考: openssl之 : crypto/rsa/rsa_pk1.c

 

对于 RSA_PKCS1_PADDING  来讲(参看: ./crypto/rsa/rsa_eay.c ):

  1. 私钥加密时,使用的是: RSA_padding_add_PKCS1_type_1(…)   pad的是定值 0xff
  2. 公钥加密时,使用的是: RSA_padding_add_PKCS1_type_2(…)   pad的是随机的非零值 (这也是为什么公钥加密每次结果不同,而私钥加密每次结果相同的原因;对于私钥加密,多半用于签名,不需要随机,或者说,相同的数据每次签名结果都相同是很好的)

Optimal Asymmetric Encryption Padding (OAEP)

OAE: 即: 最完美的非对称加密; 而OAEP 是一种padding ; 参考: http://en.wikipedia.org/wiki/Optimal_asymmetric_encryption_padding

其他padding:

 

参考资料:

rsa算法: http://www.di-mgt.com.au/rsa_alg.html

http://www.douban.com/note/338531480/

http://www.tuicool.com/articles/aMfIba

http://crypto.stackexchange.com/questions/22531/how-does-rsa-padding-work-exactly

RSA选用小公钥指数(e=3)真的不安全吗? 

RSA加密的填充模式

OAE算法

PKCS #1: RSA Cryptography Specifications Version 2.0

Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography Specifications Version 2.1

PSS:  (具体实现参看 :  pss.go)

MGF(pss 的时候需要):

 

 

PHP soap错误: “Error Fetching http headers”

缘起

PHP的soap使用有相当的时间了,今天随手给系统添加了个功能,通过soap实现的,却出现错误:”Error Fetching http headers”

 

分析

一般来讲,soap的错误可以先用tcpdump抓包看看请求响应是否正常,于是,抓包观察了一下,没有发现异常;但为什么会出现这种错误呢?百思不得姐。

应该是soapclient中抛出的错误,看看错误处的逻辑吧,翻开源码看了一下,也没有发现异常。

突然灵感就来了,大概是执行时间长所致,其实也不算长,大概5s吧!看了一下soap中关于请求响应超时的设置方式(通过default_socket_timeout 来设置),我才发现这太机器的default_socket_timeout 为 4 ; 修改大一些后,问题解决

通过ssh实现socket代理

copy from: http://blog.csdn.net/xanxus46/article/details/38942215

ssh是用来远程登录主机的作用大家都知道,而且传输的内容全部经过加密处理也是大家喜欢其的主要因素。但其实ssh能做的更多,例如作为代理服务器。

通过ssh 的-D参数,我们可以在本地搭建一个socks5服务器,具体使用方法如下:

ssh -qTNf -D 127.0.0.1:12345 test@222.333.444.555

其中-q表示该命令进入安静模式

-T是指该命令不占用shell

-N是指该命令不执行远程命令

-f是指该命令在后台运行

-D是该命令重要参数,他的后面跟着socks5服务器的地址与端口

最后就是远程服务器用户名和地址

执行完该命令后,我们需要在浏览器中设置代理服务器,地址和端口就是上面提到的,然后记得选择socks5协议。那么以后每浏览一个页面,都会先经过代理服务器,然后再去请求页面,接着页面先到达代理服务器,最后才返回我们自己的浏览器。过程好像变得复杂了,但是这样的作用是什么呢,很常见,例如有一些校园网是限制ipv4流量的,如果我们找到一台支持ipv6的远程服务器,而且校园网大部分都支持ipv6,那么我们就能通过建立ipv6代理服务器来无流量限制上网了。再者,如果我们能找到外国的免费服务器,能让我们通过ssh登录,那么我们通过建立代理服务器来翻墙了,翻墙后能做的事情就请读者自行脑补吧偷笑

接下来继续讲的是ssh的端口映射功能,这个也相当好用,例如我们在家如果想访问公司或学校的资源,我们需要先登录一台拥有公网ip的服务器,然后在访问局域网的主机,譬如,我们是A主机,需要访问学校的C主机,而只有学校B主机有公网ip,我们需要先登录B主机,然后才能访问C主机。过程很麻烦,这时我们就需要做一个端口映射了,而这个ssh也能帮我们完成。具体命令如下:

ssh -N -f -L 127.0.0.1:12345:10.21.0.34:22 test@12.3..4.5

其中-N,-f作用上面已经讲了,这里最重要的是-L命令,它作用是做本地映射,使得远程服务器的端口相当于本地某自定义的端口,如上面的命令,本地的12345端口就相当于10.21.0.34的22端口,以后我只需要使用ssh user@127.0.0.1 -p 12345命令就能登录10.21.0.34了,注意user是10.21.0.34的用户,最后就是我们所使用的中间服务器,这个地址需要是我们直接访问到的。其实上面所说的A主机就是127.0.0.1,B主机就是12.3.4.5,C主机就是10.21.0.34。当然了,有本地映射肯定有远程映射,就是把-L换成-R,这样我们访问远程主机的端口就相当于访问本地的端口,但我没发现该功能的更多用途。

如果你没有什么中间服务器,只是想做个端口映射,那也很简单,如下:

ssh -N -f -L 12345:12.3.4.5:22 test@12.3.4.5

就是把远程服务器地址写成中间服务器地址就完事了。

学习完ssh这些用法后有没有对ssh产生无比崇敬的心情的,各种膜拜啊。

 

关于socket代理 : http://blog.csdn.net/cuiyifang/article/details/8784394

近期学习总结

2014年11月21号      晴

大约从7、8月份开始,我对Go产生了兴趣,并试图用Go写一个sessionserver;9月24号,我有了儿子,鲜有时间学习,但是,我还是在伺候月子的起见熬夜完成了sessionserver的原型,测试结果基本满意。

但是,由于业务的需要,我需要写一个lua版本的sdk,于是,我的精力一下转移到了lua,由于先前对lua有所了解,所以写起来不太费劲;但是在 ffi 上还是浪费了不少的时间,大约1周的时间完成业务逻辑,使用ffi 做加密解密算法上浪费了大约2周的时间。

不管如何,我还是学到了一些东西的;此前,我不太愿意学习太多的语言,只愿精通几个足矣,通过对Go和lua的学习,让我对语言产生了兴趣,我希望能有时间学习更多的语言;不同的语言有不同的思想,不同的语言有不同的用途。

下个阶段:

  1. 主要精力放在Go上,做出一些成绩
  2. 大约20%的精力去学习其他的语言

lua ffi.string 之殇

缘起

使用ffi给lua包一个rsa算法, 刚刚学习ffi,连文档都没仔细看过,当返回加密结果时,如果直接返回CDATA,则解密没有问题,如果转为lua string(使用ffi.string) ,则后续无法解密

调试

  1. 直接在lua中调试基本无法凑效
  2. 重新编译openssl的libcrypt库,加入调试信息,先是祭出gdb,调的晕乎乎的;然后,则可以地方修改C代码,打印调试信息
  3. 发现,使用ffi.string 和不使用ffi.string 的差别在于,字符串的前面一部分是相同的,后面一部分是不同的,出于对零字节的敏感,发现是从零字节之后开始不同的;猜测ffi.string()或许可以有第二个参数(悲催,因为给一个参数也好使过,所以再没看过文档)
  4. 查ffi的文档,发现ffi.string()是有第二个参数的,添加第二个参数,问题解决
  5. 这个花费了我大约2天的时间,欲哭无泪

结论

  1. ffi.string(cdata, len) 是有第二个参数的,如果不写第二个参数,则从第一个零字节处截断
  2. 学习要循序渐进,文档是要看的

成果

 

RSA 原理

copy from: http://www.cnblogs.com/midea0978/articles/790689.html

 

RSA PKCS#1 v1.5加密标准主要描述了如何使用RSA公钥密码体系加密、解密数据,以及数字签名的算法,在网上可以直接查找到RFC的定义标准文本:
[English]
http://www.faqs.org/rfcs/rfc2313.html
[中文版本]
http://man.chinaunix.net/develop/rfc/RFC2313.txt

根据标准的定义,我们可以了解几个方面的内容:RSA公钥与私钥的构成,计算方法;加密、解密过程、数字签名过程,下面分别予以阐述:
1、RSA公钥与私钥的构成,计算方法
RSA的核心是2个大素数,p,q,描述如下:选择两个不同的奇素数p和q,以便e和(p-1)*(q-1)互素。
公开模数n是私人的素数p,q的乘积:n=p*q 。私人指数是一个正整数d,以便d*e-1可
以被(p-1)*(q-1)整除。模数n的字节长为k,k满足2^(8(k-1)) <= n < 2^(8k)。模数长度
k必须是至少12个字节,使之适应此文档中的块格式。
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, — n
publicExponent INTEGER — e }
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 }

对于根据p,q计算密钥,下面给出一段JAVA程序来实现该过程。

 1import java.math.BigInteger;
2
3public class RSAParameter {
4    private BigInteger p, q, e, n, d, dp, dq, qinv;
5
6    RSAParameter() {
7        super();
8    }
9
10    RSAParameter(String ps, String qs, String exps) {
11        init(new BigInteger(ps), new BigInteger(qs), new BigInteger(exps));
12    }
13
14    public void init(BigInteger prime1, BigInteger prime2, BigInteger exp) {
15        this.p = prime1;
16        this.q = prime2;
17        this.e = exp;
18        //n=p*q
19        n = prime1.multiply(prime2);
20        //exp*d=1 mod (p-1)(q-1)
21        d = exp.modInverse(prime1.subtract(BigInteger.ONE).multiply(prime2.subtract(BigInteger.ONE)));
22        //dp*r=1 mod (p-1) or d mod (p-1)
23        dp = d.mod(p.subtract(BigInteger.ONE));
24        //dp=exp.modInverse(p.subtract(BigInteger.ONE));    也可以这样计算dp
25        //dq*r=1 mod (q-1) or d mod (q-1)
26        dq = d.mod(q.subtract(BigInteger.ONE));
27        //q*qinv=1 mod p
28        qinv = q.modInverse(p);
29    }
30
31
32    public String toString() {
33        StringBuffer sb = new StringBuffer();
34        sb.append(“RSA Keyinfo:\n”);
35        sb.append(“PrivateKey:\n”);
36        sb.append(“modulus:” + n.toString() + “\n”);
37        sb.append(“public exponent:” + e.toString() + “\n”);
38        sb.append(“private exponent:” + d.toString() + “\n”);
39        sb.append(“prime p:” + p.toString() + “\n”);
40        sb.append(“prime q:” + q.toString() + “\n”);
41        sb.append(“prime exponent p:” + dp.toString() + “\n”);
42        sb.append(“prime exponent q:” + dq.toString() + “\n”);
43        sb.append(“crt coefficient:” + qinv.toString() + “\n”);
44        sb.append(“PublicKey:\n”);
45        sb.append(“modulus:” + n.toString() + “\n”);
46        sb.append(“public exponent:” + e.toString() + “\n”);
47        return sb.toString();
48    }
49}

2、加密、解密过程
首先需要知道密钥的长度,这是根据n的bits位来标识的,通常在java中密钥的长度k必须1024以上,也就是n应该是128 bytes.
加密,需要先将消息D格式化成EB的加密块,使EB长度=128 bytes,
EB的表述是EB = 00 || BT || PS || 00 || D
对于D的长度,不应该长于k-11个8位字节,其必为正数,因为模数的长度k是至少12个8位字节。这种限制保证了填充串PS的长度至少为8个8位字节,这是一项安全措施。
其中BT、PS值含义如下:
00 :一般不用,数据D必须以一个非0字节开始,或是必须知道长度,以便加密块能被清楚的解析。
01 :私钥操作标志,PS必须填充0xFF,通常为数字签名时使用,可以保证每次的签名数据唯一性
02 :公钥操作标志,PS必须填充随机非0数,保证每次加密的结果比一样,提高安全性

加密过程将EB转换成整数,利用公钥e,n进行加密,计算公式为
ED=EB modpow (e,n)
加密后输出的结果长度应该是固定的,长度为k/8个bytes

解密过程则刚好相反,利用私钥d,n进行解密,计算公式为
EB=ED modpow (d,n)
解密后的结果是EB,因此我们必须了解是否用公钥加密的数据,如果是,解密后数据应该是
00 01 XX … XX 00 D,其中XX是随机填充的,只要定位最后的00位置,将尾部的bytes分割下来就是明文了。
私钥加密的数据解密后数据应该是
00 02 FF …FF 00 D,原理就差不多类似了

3、数字签名
数字签名中采用如下步骤
1)计算明文hash值hashdata
2)根据ASN.1规则编码hash算法特征串+hashdata,得到签名明文数据C
3)采用RSA私钥加密C得到digest签名数据

验证过程如下
1)采用公钥解密digest,得到C
2)根据C,ASN.1解码得到hash算法特征串,获知采用什么hash算法,hashdata是什么
3)根据hash算法计算明文的hashdata1,与hashdata比较是否一致
4)根据比较结果验证签名的有效性

加解密的原理基本好理解,关键是digestAlgorithm 的特征串,根据文档定义
DigestInfo ::= SEQUENCE {
digestAlgorithm DigestAlgorithmIdentifier,
digest Digest }
MD5计算hash时,标识符就是1.2.840.113549.2.5,对于ASN.1编码,其实是一种设备独立的编码规则,在底层网络通讯中广泛被采用,一般我们可以使用
3方的包例如bouncycastle来解析或者编码
md5 OBJECT IDENTIFIER ::=
{ iso(1) member-body(2) US(840) rsadsi(113549)
digestAlgorithm(2) 5 }
下面是一段演示代码

 1import org.bouncycastle.asn1.ASN1InputStream;
2import org.bouncycastle.asn1.DERObject;
3public class  DecodeASN1
4{
5    public static void main(String[] args)
6    {
7        //这是一段RSA签名数据DigestInfo,来源参数为
8        /**//*
9        n =
10
11“11262884410355204430037697963227736559286052877012462506633640513069629804403399522764270242067801598971086784186759359440143415620433468158326754347674901462092249508308706200
12
138872607905765651105670638572435826363983713767368815675341307594277511890573814685529893908327924650039275886811260735916324764564853″;
14        d =
15
16“29921735884873348540730634487046724939153373917277871233501428959680077593491857895669424780603703806961806612978266812214830860333455470055789419709075434021826716139325709532
17
18742771186379941997345196185777640711503947179993281763887256221502624948138721356270847024512768717268039878195832083548155677542117″;
19        e = “65537”;
20        明文:s=”1234″
21        算法:MD5
22        */
23        byte[] data={48,32,48,12,6,8,42,-122,72,-122,-9,13,2,5,5,0,4,16,-127,-36,-101,-37,82,-48,77,-62,0,54,-37,-40,49,62,-48,85};
24        ASN1InputStream is=new ASN1InputStream(res);
25        DERObject obj=is.readObject();
26        System.out.println(“ASN1解码:”+obj.toString());
27        //运行结果:
28        //ASN1解码:[[1.2.840.113549.2.5, NULL], #81dc9bdb52d04dc20036dbd8313ed055]
29    }
30}

因此我们可以列出常用的hash算法标识数据备用,避免每次都要自行ASN.1编码、解码,下面列出了常见的hash标识数据
MD2:     (0x)30 20 30 0c 06 08 2a 86 48 86 f7 0d 02 02 05 00 04 10 || H
MD5:     (0x)30 20 30 0c 06 08 2a 86 48 86 f7 0d 02 05 05 00 04 10 || H
SHA-1:   (0x)30 21 30 09 06 05 2b 0e 03 02 1a 05 00 04 14 || H
SHA-256: (0x)30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || H
SHA-384: (0x)30 41 30 0d 06 09 60 86 48 01 65 03 04 02 02 05 00 04 30 || H
SHA-512: (0x)30 51 30 0d 06 09 60 86 48 01 65 03 04 02 03 05 00 04 40 || H
根据前面数据的就可以直接分析出hash算法是什么。