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

      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

       

      Golang实现

      为了方便读取pem、crt、pfx文件中的公私和私钥,这里我使用了第三方的包:golang.org/x/crypto/pkcs12,可以使用go get安装:go get -u golang.org/x/crypto/pkcs12

      安装之后,封装一个工具包rsautil.go:  

      
    package rsautil
    
    import (
        "crypto"
        "crypto/rand"
        "crypto/rsa"
        "crypto/x509"
        "crypto/x509/pkix"
        "encoding/asn1"
        "encoding/pem"
        "fmt"
        "golang.org/x/crypto/pkcs12"
        "io"
        "io/ioutil"
        "os"
        "strconv"
    )
    
    //从Pem文件中读取秘钥
    func ReadFromPem(pemFile string) ([]byte, error) {
        buffer, err := ioutil.ReadFile(pemFile)
        if err != nil {
            return nil, err
        }
    
        block, _ := pem.Decode(buffer)
        return block.Bytes, nil
    }
    
    //从pfx文件中读取公私密钥(需要安装golang.org/x/crypto/pkcs12)
    func ReadFromPfx(pfxFile, password string, usePKCS8 bool) ([]byte, []byte) {
        buffer, err := ioutil.ReadFile(pfxFile)
        if err != nil {
            panic(err)
        }
    
        privateKeyInterface, certificate, err := pkcs12.Decode(buffer, password)
        if err != nil {
            panic(err)
        }
    
        privateKey := privateKeyInterface.(*rsa.PrivateKey)
        publicKey := certificate.PublicKey.(*rsa.PublicKey)
    
        var (
            privateKeyBuffer []byte
            publicKeyBuffer  []byte
        )
        if usePKCS8 {
            privateKeyBuffer, err = x509.MarshalPKCS8PrivateKey(privateKey)
            if err != nil {
                panic(err)
            }
            publicKeyBuffer, err = x509.MarshalPKIXPublicKey(publicKey)
            if err != nil {
                panic(err)
            }
        } else {
            privateKeyBuffer = x509.MarshalPKCS1PrivateKey(privateKey)
            publicKeyBuffer = x509.MarshalPKCS1PublicKey(publicKey)
        }
        return publicKeyBuffer, privateKeyBuffer
    }
    
    //从crt中读取公钥
    func ReadPublicKeyFromCrt(crtFile string, usePKCS8 bool) ([]byte, error) {
        buffer, err := ioutil.ReadFile(crtFile)
        if err != nil {
            return nil, err
        }
        certDERBlock, _ := pem.Decode(buffer)
        certificate, err := x509.ParseCertificate(certDERBlock.Bytes)
        if err != nil {
            return nil, err
        }
        publicKey := certificate.PublicKey.(*rsa.PublicKey)
    
        var publicKeyBuffer []byte
        if usePKCS8 {
            publicKeyBuffer, err = x509.MarshalPKIXPublicKey(publicKey)
        } else {
            publicKeyBuffer = x509.MarshalPKCS1PublicKey(publicKey)
        }
        if err != nil {
            return nil, err
        }
        return publicKeyBuffer, nil
    }
    
    //将秘钥写入Pem文件
    func WriteToPem(isPrivateKey bool, buffer []byte, pemFile string) error {
        var _type string
        if isPrivateKey {
            _type = "RSA PRIVATE KEY"
        } else {
            _type = "RSA PUBLIC KEY"
        }
    
        block := &pem.Block{
            Type:  _type, //这个字符串随便写
            Bytes: buffer,
        }
    
        file, err := os.Create(pemFile)
        if err != nil {
            return err
        }
        return pem.Encode(file, block)
    }
    
    //Pkcs1转换为Pkcs8
    func Pkcs1ToPkcs8(isPrivateKey bool, buffer []byte) []byte {
        var (
            oid  = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
            info interface{}
        )
        if isPrivateKey {
            val := struct {
                Version    int
                Algo       []asn1.ObjectIdentifier
                PrivateKey []byte
            }{}
            val.Version = 0
            val.Algo = []asn1.ObjectIdentifier{oid}
            val.PrivateKey = buffer
            info = val
        } else {
            val := struct {
                Algo      pkix.AlgorithmIdentifier
                BitString asn1.BitString
            }{}
            val.Algo.Algorithm = oid
            val.Algo.Parameters = asn1.NullRawValue
            val.BitString.Bytes = buffer
            val.BitString.BitLength = 8 * len(buffer)
            info = val
        }
    
        b, err := asn1.Marshal(info)
        if err != nil {
            panic(err)
        }
        return b
    }
    
    //Pkcs8转换为Pkcs1
    func Pkcs8ToPkcs1(isPrivateKey bool, buffer []byte) []byte {
        if isPrivateKey {
            val := struct {
                Version    int
                Algo       pkix.AlgorithmIdentifier
                PrivateKey []byte
            }{}
            _, err := asn1.Unmarshal(buffer, &val)
            if err != nil {
                panic(err)
            }
            return val.PrivateKey
        } else {
            val := struct {
                Algo      pkix.AlgorithmIdentifier
                BitString asn1.BitString
            }{}
    
            _, err := asn1.Unmarshal(buffer, &val)
            if err != nil {
                panic(err)
            }
            return val.BitString.Bytes
        }
    }
    
    //生成公私钥
    //usePKCS8:是否使用pkcs8
    func GenerateRsaKey(usePKCS8 bool) ([]byte, []byte) {
        //生成私钥
        privateKey, err := rsa.GenerateKey(rand.Reader, 1024) //1024位
        if err != nil {
            panic(err)
        }
        //公钥
        publicKey := privateKey.PublicKey
    
        var (
            privateKeyBuffer []byte
            publicKeyBuffer  []byte
        )
    
        if usePKCS8 {
            privateKeyBuffer, err = x509.MarshalPKCS8PrivateKey(privateKey)
            if err != nil {
                panic(err)
            }
            publicKeyBuffer, err = x509.MarshalPKIXPublicKey(&publicKey)
            if err != nil {
                panic(err)
            }
        } else {
            privateKeyBuffer = x509.MarshalPKCS1PrivateKey(privateKey)
            publicKeyBuffer = x509.MarshalPKCS1PublicKey(&publicKey)
        }
    
        return publicKeyBuffer, privateKeyBuffer
    }
    
    func parsePkcsKey(buffer []byte, isPrivateKey, usePKCS8 bool) (interface{}, error) {
        var (
            err          error
            keyInterface interface{}
        )
    
        if isPrivateKey {
            if usePKCS8 {
                keyInterface, err = x509.ParsePKCS8PrivateKey(buffer)
            } else {
                keyInterface, err = x509.ParsePKCS1PrivateKey(buffer)
            }
        } else {
            if usePKCS8 {
                keyInterface, err = x509.ParsePKIXPublicKey(buffer)
            } else {
                keyInterface, err = x509.ParsePKCS1PublicKey(buffer)
            }
        }
        if err != nil {
            return nil, err
        }
        return keyInterface, nil
    }
    
    //RSA加密
    func RsaEncrypt(value string, publicKey []byte, usePKCS8 bool) (string, error) {
        keyInterface, err := parsePkcsKey(publicKey, false, usePKCS8)
        if err != nil {
            return "", err
        }
        rsaPublicKey := keyInterface.(*rsa.PublicKey)
        buffer, err := rsa.EncryptPKCS1v15(rand.Reader, rsaPublicKey, []byte(value))
        if err != nil {
            return "", err
        }
    
        //以hex格式数值输出
        encryptText := fmt.Sprintf("%x", buffer)
        return encryptText, nil
    }
    
    //RSA解密
    func RsaDecrypt(value string, privateKey []byte, usePKCS8 bool) (string, error) {
        //将hex格式数据转换为byte切片
        valueBytes := []byte(value)
        var buffer = make([]byte, len(valueBytes)/2)
        for i := 0; i < len(buffer); i++ {
            b, err := strconv.ParseInt(value[i*2:i*2+2], 16, 10)
            if err != nil {
                return "", err
            }
            buffer[i] = byte(b)
        }
    
        keyInterface, err := parsePkcsKey(privateKey, true, usePKCS8)
        if err != nil {
            return "", err
        }
        key := keyInterface.(*rsa.PrivateKey)
        buffer, err = rsa.DecryptPKCS1v15(rand.Reader, key, buffer)
        return string(buffer), nil
    }
    
    //RSA签名
    func Sign(value string, privateKey []byte, hash crypto.Hash, usePKCS8 bool) (string, error) {
        keyInterface, err := parsePkcsKey(privateKey, true, usePKCS8)
        if err != nil {
            return "", err
        }
        key := keyInterface.(*rsa.PrivateKey)
    
        var _hash = hash.New()
        if _, err := io.WriteString(_hash, value); err != nil {
            return "", err
        }
    
        hashed := _hash.Sum(nil)
        result, err := rsa.SignPKCS1v15(rand.Reader, key, hash, hashed)
        if err != nil {
            return "", err
        }
    
        //以hex格式数值输出
        encryptText := fmt.Sprintf("%x", result)
        return encryptText, nil
    }
    
    //RSA验证签名
    func Verify(value string, publicKey []byte, signature string, hash crypto.Hash, usePKCS8 bool) error {
        //将hex格式数据转换为byte切片
        valueBytes := []byte(signature)
        var buffer = make([]byte, len(valueBytes)/2)
        for i := 0; i < len(buffer); i++ {
            b, err := strconv.ParseInt(signature[i*2:i*2+2], 16, 10)
            if err != nil {
                return err
            }
            buffer[i] = byte(b)
        }
    
        keyInterface, err := parsePkcsKey(publicKey, false, usePKCS8)
        if err != nil {
            return err
        }
    
        key := keyInterface.(*rsa.PublicKey)
    
        var _hash = hash.New()
        if _, err := io.WriteString(_hash, value); err != nil {
            return err
        }
    
        hashed := _hash.Sum(nil)
        return rsa.VerifyPKCS1v15(key, hash, hashed, buffer)
    }
    rsautil

      可以使用生成RSA的公私秘钥:

        //生成Rsa
        publicKey, privateKey := rsautil.GenerateRsaKey(usePKCS8)

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

        //保存到Pem文件,filePath是文件目录
        rsautil.WriteToPem(false, publicKey, filepath.Join(filePath, "rsa.pub"))
        rsautil.WriteToPem(true, privateKey, filepath.Join(filePath, "rsa.pem"))

      从pem文件中读取:  

        //从Pem文件读取秘钥,filePath是文件目录
        publicKey, _ := rsautil.ReadFromPem(filepath.Join(filePath, "rsa.pub"))
        privateKey, _ := rsautil.ReadFromPem(filepath.Join(filePath, "rsa.pem"))

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

        //从crt文件中读取公钥,filePath是文件目录
        publicKey, _ := rsautil.ReadPublicKeyFromCrt(filepath.Join(filePath, "demo.crt"), usePKCS8)
        privateKey, _ := rsautil.ReadFromPem(filepath.Join(filePath, "demo.key"))

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

        //从pfx文件中读取秘钥,filePath是文件目录
        publicKey, privateKey := rsautil.ReadFromPfx(filepath.Join(filePath, "demo.pfx"), "123456", usePKCS8)

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

        //Pkcs8格式公钥转换为Pkcs1格式公钥
        publicKey = rsautil.Pkcs8ToPkcs1(false, publicKey)
        // Pkcs8格式私钥转换为Pkcs1格式私钥
        privateKey = rsautil.Pkcs8ToPkcs1(true, privateKey)
        // Pkcs1格式公钥转换为Pkcs8格式公钥
        publicKey = rsautil.Pkcs1ToPkcs8(false, publicKey)
        // Pkcs1格式私钥转换为Pkcs8格式私钥
        privateKey = rsautil.Pkcs1ToPkcs8(true, privateKey)

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

        encryptText, _ := rsautil.RsaEncrypt(text, publicKey, usePKCS8)
        fmt.Printf("【%s】经过【RSA】加密后:%s
    ", text, encryptText)
    
        decryptText, _ := rsautil.RsaDecrypt(encryptText, privateKey, usePKCS8)
        fmt.Printf("【%s】经过【RSA】解密后:%s
    ", encryptText, decryptText)
    
        signature, _ := rsautil.Sign(text, privateKey, crypto.MD5, usePKCS8)
        fmt.Printf("【%s】经过【RSA】签名后:%s
    ", text, signature)
    
        result := rsautil.Verify(text, publicKey, signature, crypto.MD5, usePKCS8) == nil
        fmt.Printf("【%s】的签名【%s】经过【RSA】验证后结果是:"+strconv.FormatBool(result), text, signature)

      完整的demo代码:

    package main
    
    import (
        "crypto"
        "demo/rsautil"
        "fmt"
        "os"
        "path/filepath"
        "strconv"
    )
    
    func main() {
        text := "上山打老虎"
        usePKCS8 := true // usePKCS8=true表示是否成PKCS8格式的公私秘钥,否则乘车PKCS1格式的公私秘钥
        path, _ := os.Executable()
        filePath := filepath.Dir(path)
        fmt.Printf("文件路径:%s
    ", filePath) // 存放pem,crt,pfx等文件的目录
    
        //生成Rsa
        publicKey, privateKey := rsautil.GenerateRsaKey(usePKCS8)
        //从Pem文件读取秘钥,filePath是文件目录
        //publicKey, _ := rsautil.ReadFromPem(filepath.Join(filePath, "rsa.pub"))
        //privateKey, _ := rsautil.ReadFromPem(filepath.Join(filePath, "rsa.pem"))
        //从pfx文件中读取秘钥,filePath是文件目录
        //publicKey, privateKey := rsautil.ReadFromPfx(filepath.Join(filePath, "demo.pfx"), "123456", usePKCS8)
        //从crt文件中读取公钥,filePath是文件目录
        //publicKey, _ := rsautil.ReadPublicKeyFromCrt(filepath.Join(filePath, "demo.crt"), usePKCS8)
        //privateKey, _ := rsautil.ReadFromPem(filepath.Join(filePath, "demo.key"))
    
        //保存到Pem文件,filePath是文件目录
        rsautil.WriteToPem(false, publicKey, filepath.Join(filePath, "rsa.pub"))
        rsautil.WriteToPem(true, privateKey, filepath.Join(filePath, "rsa.pem"))
    
        //Pkcs8格式公钥转换为Pkcs1格式公钥
        publicKey = rsautil.Pkcs8ToPkcs1(false, publicKey)
        // Pkcs8格式私钥转换为Pkcs1格式私钥
        privateKey = rsautil.Pkcs8ToPkcs1(true, privateKey)
        // Pkcs1格式公钥转换为Pkcs8格式公钥
        publicKey = rsautil.Pkcs1ToPkcs8(false, publicKey)
        // Pkcs1格式私钥转换为Pkcs8格式私钥
        privateKey = rsautil.Pkcs1ToPkcs8(true, privateKey)
    
        encryptText, _ := rsautil.RsaEncrypt(text, publicKey, usePKCS8)
        fmt.Printf("【%s】经过【RSA】加密后:%s
    ", text, encryptText)
    
        decryptText, _ := rsautil.RsaDecrypt(encryptText, privateKey, usePKCS8)
        fmt.Printf("【%s】经过【RSA】解密后:%s
    ", encryptText, decryptText)
    
        signature, _ := rsautil.Sign(text, privateKey, crypto.MD5, usePKCS8)
        fmt.Printf("【%s】经过【RSA】签名后:%s
    ", text, signature)
    
        result := rsautil.Verify(text, publicKey, signature, crypto.MD5, usePKCS8) == nil
        fmt.Printf("【%s】的签名【%s】经过【RSA】验证后结果是:"+strconv.FormatBool(result), text, signature)
    }
    一个专注于.NetCore的技术小白
  • 相关阅读:
    多字段截取然后验证 js
    web端上传图片,截取证件照
    bootstrap学习
    响应式网站设计心得
    不懂这几个问题就落后了:Python、Android开发者必读!
    这48个Java技术点,让你的面试成功率提升5倍!
    C++之父谈C++:一天之内你就能学会出色使用C++
    常用正则表达式
    解决vs验证控件报错” WebForms UnobtrusiveValidationMode 需要“jquery”ScriptResourceMapping。请添加一个名为 jquery (区分大小写)的 ScriptResourceMapping”问题
    怎么就那么难啊我去
  • 原文地址:https://www.cnblogs.com/shanfeng1000/p/14840061.html
Copyright © 2011-2022 走看看