zoukankan      html  css  js  c++  java
  • RSA非对称加密算法实现:Python

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

      RSA解决了对称加密的一个不足,比如AES算法加密和解密时使用的是同一个秘钥,因此这个秘钥不能公开,因此对于需要公开秘钥的场合,我们需要在加密和解密过程中使用不同的秘钥,加密使用的公钥可以公开,解密使用的私钥要保密,这就是非对称加密的好处。 

      常用的开发语言来实现RSA加密:

      RSA非对称加密算法实现:Java

      RSA非对称加密算法实现:C#

      RSA非对称加密算法实现:Golang

      RSA非对称加密算法实现:Python

     

      公钥与私钥

      公钥与私钥是成对的,一般的,我们认为的是公钥加密、私钥解密、私钥签名、公钥验证,有人说成私钥加密,公钥解密时不对的。

      公钥与私钥的生成有多种方式,可以通过程序生成(下文具体实现),可以通过openssl工具:  

        # 生成一个私钥,推荐使用1024位的秘钥,秘钥以pem格式保存到-out参数指定的文件中,采用PKCS1格式
        openssl genrsa -out rsa.pem 1024 
        # 生成与私钥对应的公钥,生成的是Subject Public Key,一般配合PKCS8格式私钥使用
        openssl rsa -in rsa.pem -pubout -out rsa.pub  

      RSA生成公钥与私钥一般有两种格式:PKCS1和PKCS8,上面的命令生成的秘钥是PKCS1格式的,而公钥是Subject Public Key,一般配合PKCS8格式私钥使用,所以就可能会涉及到PKCS1和PKCS8之间的转换:

        # PKCS1格式私钥转换为PKCS8格式私钥,私钥直接输出到-out参数指定的文件中
        openssl pkcs8 -topk8 -inform PEM -in rsa.pem -outform pem -nocrypt -out rsa_pkcs8.pem
        # PKCS8格式私钥转换为PKCS1格式私钥,私钥直接输出到-out参数指定的文件中
        openssl rsa -in rsa_pkcs8.pem -out rsa_pkcs1.pem
    
        # PKCS1格式公钥转换为PKCS8格式公钥,转换后的内容直接输出
        openssl rsa -pubin -in rsa.pub -RSAPublicKey_out
        # PKCS8格式公钥转换为PKCS1格式公钥,转换后的内容直接输出
        openssl rsa -RSAPublicKey_in -pubout -in rsa.pub

      现实中,我们往往从pem、crt、pfx文件获取公私和私钥,crt、pfx的制作可以参考:简单的制作ssl证书,并在nginx和IIS中使用,或者使用现成的:https://pan.baidu.com/s/1MJ5YmuZiLBnf-DfNR_6D7A (提取码:c6tj),密码都是:123456

      Python实现

      首先使用pip两个第三方包:  

        # 用于从crt、pfx等文件读取公私秘钥
        pip install pyOpenSSL
        # 用RSA加密解密签名验证等(如果安装不了,先卸载旧版本再安装:pip uninstall pycrypto)
        pip install pycryptodome

      接着封装一个模块RsaUtil:  

      
    # 需要安装OpenSSL包:pip install pyOpenSSL
    # 需要安装pycrypto包:pip install pycryptodome
    from Crypto import Random, Hash
    from Crypto.PublicKey import RSA
    from Crypto.Cipher import PKCS1_v1_5 as PKCS1_v1_5_Cipher
    from Crypto.Signature import PKCS1_v1_5 as PKCS1_v1_5_Signature
    from Crypto.IO import PEM, PKCS8
    from OpenSSL import crypto
    from Crypto.Util.asn1 import (DerSequence, DerBitString, DerObjectId, DerNull)
    
    
    # pkcs1格式转换为pkcs8
    def pkcs1_to_pkcs8(buffer):
        rsakey = RSA.importKey(buffer)
        if rsakey.has_private():
            return PKCS8.wrap(buffer, RSA.oid, None)
        return rsakey.exportKey("DER")
    
    
    # pkcs8格式转换为pkcs1
    def pkcs8_to_pkcs1(buffer):
        rsakey = RSA.importKey(buffer)
        if rsakey.has_private():  # 私钥
            return PKCS8.unwrap(buffer, None)[1]
    
        spki = DerSequence().decode(buffer, nr_elements=2)
        algo = DerSequence().decode(spki[0], nr_elements=(1, 2))
        algo_oid = DerObjectId().decode(algo[0])
        spk = DerBitString().decode(spki[1]).value
    
        if len(algo) == 1:
            algo_params = None
        else:
            try:
                DerNull().decode(algo[1])
                algo_params = None
            except:
                algo_params = algo[1]
    
        if algo_oid.value != RSA.oid or algo_params is not None:
            raise ValueError("No RSA subjectPublicKeyInfo")
        return spk
    
    
    # 生成RSA加密的公私密钥
    def generate_rsa_key(use_pkcs8=False):
        rsa_key = RSA.generate(1024, Random.new().read)  # 1024位
        private_key = rsa_key.exportKey("DER", pkcs=1)
        public_key = rsa_key.publickey().exportKey("DER")  # subject_public_key
        if use_pkcs8:
            private_key = pkcs1_to_pkcs8(private_key)
        else:
            public_key = pkcs8_to_pkcs1(public_key)
    
        return public_key, private_key
    
    
    # 从pem文件中读取秘钥
    def read_from_pem(pen_file_ame):
        with open(pen_file_ame, 'r') as f:
            t = PEM.decode(f.read())
            if t[2]:
                raise ValueError("fail to read pem")
            return t[0]
    
    
    # 将秘钥写入pem文件
    def write_to_pem(buffer, is_private_key, pen_file_ame):
        if is_private_key:
            str = PEM.encode(buffer, "RSA PRIVATE KEY")
        else:
            str = PEM.encode(buffer, "RSA PUBLIC KEY")
    
        with open(pen_file_ame, 'w') as f:
            f.write(str)
    
    
    # 从crt文件中读取公钥
    def read_public_key_from_crt(crt_file_name):
        with open(crt_file_name, 'rb') as f:
            cert = crypto.load_certificate(crypto.FILETYPE_PEM, f.read())
            return crypto.dump_publickey(crypto.FILETYPE_PEM, cert.get_pubkey())
    
    
    # 从pfx文件中读取公私钥
    def read_from_pfx(pfx_file_name, password):
        with open(pfx_file_name, 'rb') as f:
            pfx = crypto.load_pkcs12(f.read(), bytes(password, encoding="utf-8"))
            cert = pfx.get_certificate()
            _public_key = crypto.dump_publickey(crypto.FILETYPE_PEM, cert.get_pubkey())
            _privat_key = crypto.dump_privatekey(crypto.FILETYPE_PEM, pfx.get_privatekey())
            return _public_key, _privat_key
    
    
    # RSA使用公钥加密
    def rsa_encrypt(value, public_key):
        rsakey = RSA.importKey(public_key)
        cipher = PKCS1_v1_5_Cipher.new(rsakey)
        buffer = cipher.encrypt(value.encode("utf-8"))
        return buffer.hex()  # 使用hex格式输出
    
    
    # RSA使用私钥解密
    def rsa_decrypt(value, private_key):
        rsakey = RSA.importKey(private_key)
        cipher = PKCS1_v1_5_Cipher.new(rsakey)
        buffer = bytes.fromhex(value)  # 读取hex格式数据
        buffer = cipher.decrypt(buffer, Random.new().read)
        return buffer.decode("utf-8")
    
    
    # RSA使用私钥签名
    def sign(value, private_ey, halg=Hash.MD5):
        rsakey = RSA.importKey(private_ey)
        signer = PKCS1_v1_5_Signature.new(rsakey)
    
        hash = halg.new()
        hash.update(value.encode("utf-8"))
        buffer = signer.sign(hash)
        return buffer.hex()  # 使用hex格式输出
    
    
    # RSA使用公钥验证签名
    def verify(value, public_key, signature, halg=Hash.MD5):
        rsakey = RSA.importKey(public_key)
        verifier = PKCS1_v1_5_Signature.new(rsakey)
    
        hash = halg.new()
        hash.update(value.encode("utf-8"))
    
        buffer = bytes.fromhex(signature)  # 读取hex格式数据
        result: bool = verifier.verify(hash, buffer)
        return result
    RsaUtil

      生成RSA的公私秘钥:  

    # 生成rsa公私秘钥
    (publicKey, privateKey) = RsaUtil.generate_rsa_key(use_pkcs8)

      生成秘钥后,需要保存,一般保存到pem文件中:  

    # 将公私秘钥写入pem文件,filePath是文件目录
    RsaUtil.write_to_pem(publicKey, False, os.path.join(filePath, "rsa.pub"))
    RsaUtil.write_to_pem(privateKey, True, os.path.join(filePath, "rsa.pem"))

      可以保存到pem文件中,当然也可以从pem文件中读取了:  

    # 从pem文件中读取秘钥,filePath是文件目录
    publicKey = RsaUtil.read_from_pem(os.path.join(filePath, "rsa.pub"))
    privateKey = RsaUtil.read_from_pem(os.path.join(filePath, "rsa.pem"))

      还可以从crt证书中读取公钥,而crt文件不包含私钥,因此需要单独获取私钥:

    # 从crt文件读取,filePath是文件目录
    publicKey = RsaUtil.read_public_key_from_crt(os.path.join(filePath, "demo.crt"))
    privateKey = RsaUtil.read_from_pem(os.path.join(filePath, "demo.key"))

      pfx文件中包含了公钥和私钥,可以很方便就读取到:

    # 从demo.pfx文件读取(demo.pfx采用的是pkcs1),filePath是文件目录
    (publicKey, privateKey) = RsaUtil.read_from_pfx(os.path.join(filePath, "demo.pfx"), "123456")

      有时候我们还可能需要进行秘钥的转换:

    # Pkcs8格式公钥转换为Pkcs1格式公钥
    publicKey = RsaUtil.pkcs8_to_pkcs1(publicKey)
    # Pkcs8格式私钥转换为Pkcs1格式私钥
    privateKey = RsaUtil.pkcs8_to_pkcs1(privateKey)
    # Pkcs1格式公钥转换为Pkcs8格式公钥
    publicKey = RsaUtil.pkcs1_to_pkcs8(publicKey)
    # Pkcs1格式私钥转换为Pkcs8格式私钥
    privateKey = RsaUtil.pkcs1_to_pkcs8(privateKey)

      有了公钥和私钥,接下就就能实现加密、解密、签名、验证签名等操作了:

    encryptText = RsaUtil.rsa_encrypt(text, publicKey)
    print("", text, "】经过【RSA】加密后:", encryptText)
    
    decryptText = RsaUtil.rsa_decrypt(encryptText, privateKey)
    print("", encryptText, "】经过【RSA】解密后:", decryptText)
    
    signature = RsaUtil.sign(text, privateKey, Hash.MD5)
    print("", text, "】经过【RSA】签名后:", signature)
    
    result = RsaUtil.verify(text, publicKey, signature, Hash.MD5)
    print("", text, "】的签名【", signature, "】经过【RSA】验证后结果是:", result)

      完整的demo代码:

    import RsaUtil
    from Crypto import Hash
    import os
    
    text = "上山打老虎"
    use_pkcs8 = True
    filePath = os.getcwd()
    print("文件路径:", filePath)
    
    # 生成rsa公私秘钥
    (publicKey, privateKey) = RsaUtil.generate_rsa_key(use_pkcs8)
    # 从pem文件中读取秘钥,filePath是文件目录
    # publicKey = RsaUtil.read_from_pem(os.path.join(filePath, "rsa.pub"))
    # privateKey = RsaUtil.read_from_pem(os.path.join(filePath, "rsa.pem"))
    # 从demo.pfx文件读取(demo.pfx采用的是pkcs1),filePath是文件目录
    # (publicKey, privateKey) = RsaUtil.read_from_pfx(os.path.join(filePath, "demo.pfx"), "123456")
    # 从crt文件读取,filePath是文件目录
    # publicKey = RsaUtil.read_public_key_from_crt(os.path.join(filePath, "demo.crt"))
    # privateKey = RsaUtil.read_from_pem(os.path.join(filePath, "demo.key"))
    
    # 将公私秘钥写入pem文件,filePath是文件目录
    RsaUtil.write_to_pem(publicKey, False, os.path.join(filePath, "rsa.pub"))
    RsaUtil.write_to_pem(privateKey, True, os.path.join(filePath, "rsa.pem"))
    
    # Pkcs8格式公钥转换为Pkcs1格式公钥
    publicKey = RsaUtil.pkcs8_to_pkcs1(publicKey)
    # Pkcs8格式私钥转换为Pkcs1格式私钥
    privateKey = RsaUtil.pkcs8_to_pkcs1(privateKey)
    # Pkcs1格式公钥转换为Pkcs8格式公钥
    publicKey = RsaUtil.pkcs1_to_pkcs8(publicKey)
    # Pkcs1格式私钥转换为Pkcs8格式私钥
    privateKey = RsaUtil.pkcs1_to_pkcs8(privateKey)
    
    encryptText = RsaUtil.rsa_encrypt(text, publicKey)
    print("", text, "】经过【RSA】加密后:", encryptText)
    
    decryptText = RsaUtil.rsa_decrypt(encryptText, privateKey)
    print("", encryptText, "】经过【RSA】解密后:", decryptText)
    
    signature = RsaUtil.sign(text, privateKey, Hash.MD5)
    print("", text, "】经过【RSA】签名后:", signature)
    
    result = RsaUtil.verify(text, publicKey, signature, Hash.MD5)
    print("", text, "】的签名【", signature, "】经过【RSA】验证后结果是:", result)
    一个专注于.NetCore的技术小白
  • 相关阅读:
    android实现简单计算器
    象牙塔尖
    Jquery 限制文本框输入字数【转】
    jquery 文字向上滚动+CSS伪类before和after的应用
    鼠标移入 移出div div会消失的处理
    ionic 项目分享【转】
    JS+CSS简单实现DIV遮罩层显示隐藏【转藏】
    封装鼠标滚轮事件,兼容方法。。。拿去用吧
    3、bootstrap3.0 栅格偏移 布局中的一个特产
    html input[type=file] css样式美化【转藏】
  • 原文地址:https://www.cnblogs.com/shanfeng1000/p/14840063.html
Copyright © 2011-2022 走看看