唠叨两句,蹭点篇幅

RSA是一种非对称加密算法,简单理解就是两个密钥:一个公钥,一个私钥。 
同时它也可以用来签名和验签,正好与加密相反。

  • 加密:公钥加密,私钥解密;
  • 签名:私钥签名,公钥验签。

有意思的是有些人分不清公钥私钥的用处,这里提供一个思路: 
用作加密时,密文泄露是无所谓的(相对而言),重要的是用于解密的密钥必须安全,所以用不公开的私钥来解密,用公钥来加密; 
用作签名时,目的是防止别人伪造我的身份发信息,所以用私钥来签名,用公钥来验签。

对于加密及签名的讲解,看过一篇很棒的翻译,看完过后非常有助于理解,并且图文并茂,很生动: 
数字签名是什么? - 阮一峰的网络日志

@@流程:

数字签名

Http请求为例, 发送报文时,发送方用一个Hash函数从报文中生成报文摘要,然后用自己的私人密钥对这个摘要进行加密,这个加密后的摘要将要作为报文的数字签名和报文一起发送给接收方,接收方首先用与发送方一样的Hash函数从接收到的原始报文中计算出报文的摘要,接着再用发送发送方的公用密钥对报文附加的数字签名进行解密,如果这两段摘要一致,则接收方就可以确认该数字签名是发送方的

数字证书

数字证书包含以下信息:

  • X.509版本号,指出该证书使用了哪种版本的X.509标准
  • 证书持有人的公钥,包括证书持有人的公钥、算法(指明密钥属于哪种密码系统的标识符以及相关参数)
  • 证书的序列号,由CA给予每个证书分配的唯一的数字型编号,当证书被取消时,实际上是将此证书序列号放入由CA签发的CRL(Certificate Revocation List 证书作废表)
  • 主题信息:证书持有人的唯一标识符,这些信息指出该科目的通用单位名,组织单位,组织和国家或者证书持有人的姓名,服务处等信息
  • 证书的有效期
  • 认证机构,证书发布者,是签发该证书的实体唯一的X.500名字,使用该证书意味着新人签发整数的实体
  • 发布者的数字签名,这时使用发布者私钥生成的签名,以确保这个证书在发放之后没有被篡改过
  • 签名算法标标识符,用来指定CA签署证书时所使用的签名算法

数字证书颁发过程

  1. 申请者生成密钥对,自己保存私钥
  2. 将公钥和身份信息发送给CA
  3. CA验明身份
  4. CA对接收到的报文(即公钥和身份信息)进行Hash摘要
  5. 使用私钥对摘要进行加密签名,形成数字签名
  6. 生成数字证书发送给申请者
  7. 申请者对之前发送的报文(公钥和身份信息)使用同样的Hash算法进行摘要
  8. 申请者用CA的公钥对返回来的内容进行解密,之后同第7步生成的摘要进行比对,如果相同则代表证书无误

一、签名

Crypto不是自带的模块,需要下载。http://www.voidspace.org.uk/python/modules.shtml#pycrypto

话不多说,直接贴代码:

from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import MD5
import base64
def RSA_sign(encrData):
    privateKey = '''MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOG/8J9TP8cN7upNoZ+LoBs9xImv
4hHAwO/gq7TLGjZ5IoUIxWPJbGtUI+muXsFf8tBfjE3p86ava1R1Ji0c0Sh98bPT4lFMqGFWV5OJ
d2VbvLGG4DclFzkZxMuB4M7sSvXlKdfawHuFFG/HiEEzjuiROfqWlP3qZ6Ix0QLRhE9HAgMBAAEC
gYBdPujnBn3rfIfY4+QEgKnLVsIdlTat2o5XBtglv1a+dV6a0LqnswVDd+e1mD6vZTBofW74p8/q
Y77TjegM7kA90Nw9N4z2uuhn7kXFNI+RiA2MUXcqf4Vwb/64wRpqH70abZzCuyhxQYXqNqEmJuL4
jAxMoxztxj4BvXt5zk9ekQJBAPLwghERrLNgu+ty/Fmdk15NWE/Eoazig15THPEgrZ5Ruaq90U9O
4sTWbYgDLYJI75uDTgFoPE+VHkT40WspYZMCQQDt4tsPwVHtXEX4sYclEAbWzXEYDlNXCA3zvWf6
9cb9N+oY4FfMuThFdfpC5H2D+5bhqCRLZUzuvthS7i18ljv9AkEApJkFVvFFtIc+60iN513XAhaf
VfRgohUacqcXPdwpJdIzXJadIQHOrRSnQ3b7t4EZLqFpEZUA/96Fkq+Om+9+lwJBAK0fjwt9RrF2
mNmv4UnAyyliZC78pfxNuVGsg1LpsYKxQaYPBvbPyTsL7DDodswpuhnJs3hHZeDOdUKNYf8smsUC
QDhQqQHjf6Kf9ZI/zO6Ldvn0y5cMomzfFaH8ltRcjuNB8num1Vt0Oyk+k90q+OYak5twRvKGGsQD
8v+gOIQ8ZfA='''
    private_keyBytes = base64.b64decode(privateKey)
    priKey = RSA.importKey(private_keyBytes)
    #priKey = RSA.importKey(privateKey)
    signer = PKCS1_v1_5.new(priKey)
    hash_obj = MD5.new(data.encode('utf-8'))
    signature = base64.b64encode(signer.sign(hash_obj))
    return signature

几个注意事项: 
1、密钥如果是读取自.pem文件,密钥会有开始行和结束行,叫做头标注信息和尾标注信息。常见的长这样:

'''-----BEGIN PRIVATE KEY-----
#密钥内容#
-----END PRIVATE KEY-----'''

此时直接priKey = RSA.importKey(privateKey)(见被注释掉的部分)即可,不用对私钥进行base64解码; 
2、哈希算法可以采用MD5,也可以用别的比如SHA; 
3、data是需要签名的数据,需要字节化后才能传进MD5.new()中。字节化有三种方法,示例中采用了第三种:

  1. b'zifuchuang'
  2. bytes('zifuchuang',encoding='utf-8')
  3. 'zifuchuang'.encode('utf-8')

二、验签

def verify(signature,encrData):
    publicKey = '''MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCJ3haUf1Tj0/XuknJqsxg/N8nu3NqXhvo6Mdbw
UFuLYux0pjKlY2lCvV14Rlnl2bf0XoIaFcHZIvbexv8bXfakDhpQokyToKvK6kIlbkgKCqCzjVm0
ZthNou5cnqVyRurn3UXxELn1VfHkLc4nSZie+pMwiBRB4ViuAH1+w/gyvwIDAQAB'''
    public_keyBytes = base64.b64decode(publicKey)
    pubKey = RSA.importKey(public_keyBytes)
    #pubKey = RSA.importKey(publicKey)
    h = MD5.new(data.encode('utf-8'))
    verifier = PKCS1_v1_5.new(pubKey)
    return verifier.verify(h, base64.b64decode(signature))

注意事项: 
1、因为签名时我们对RSA_sign()返回的签名值进行过过base64编码,所以验签时需要解码; 
2、函数verifier.verify()返回的是bool值,在外层可以直接用作条件判断。

三、后话

其实RSA更广泛的用法是加密,懒癌发作,以后再搞吧,因为要讲的东西实在是蛮多的-。