RSA是1977年由羅納德·李維斯特(Ron Rivest)、阿迪·薩莫爾(Adi Shamir)和倫納德·阿德曼(Leonard Adleman)一起提出的。當時他們三人都在麻省理工學院工作。RSA就是他們三人姓氏開頭字母拼在一起組成的。
RSA解決了對稱加密的一個不足,比如AES算法加密和解密時使用的是同一個秘鑰,因此這個秘鑰不能公開,因此對於需要公開秘鑰的場合,我們需要在加密和解密過程中使用不同的秘鑰,加密使用的公鑰可以公開,解密使用的私鑰要保密,這就是非對稱加密的好處。
常用的開發語言來實現RSA加密:
公鑰與私鑰
公鑰與私鑰是成對的,一般的,我們認為的是公鑰加密、私鑰解密、私鑰簽名、公鑰驗證,有人說成私鑰加密,公鑰解密時不對的。
公鑰與私鑰的生成有多種方式,可以通過程序生成(下文具體實現),可以通過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) }
可以使用生成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\n", text, encryptText) decryptText, _ := rsautil.RsaDecrypt(encryptText, privateKey, usePKCS8) fmt.Printf("【%s】經過【RSA】解密后:%s\n", encryptText, decryptText) signature, _ := rsautil.Sign(text, privateKey, crypto.MD5, usePKCS8) fmt.Printf("【%s】經過【RSA】簽名后:%s\n", 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\n", 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\n", text, encryptText) decryptText, _ := rsautil.RsaDecrypt(encryptText, privateKey, usePKCS8) fmt.Printf("【%s】經過【RSA】解密后:%s\n", encryptText, decryptText) signature, _ := rsautil.Sign(text, privateKey, crypto.MD5, usePKCS8) fmt.Printf("【%s】經過【RSA】簽名后:%s\n", text, signature) result := rsautil.Verify(text, publicKey, signature, crypto.MD5, usePKCS8) == nil fmt.Printf("【%s】的簽名【%s】經過【RSA】驗證后結果是:"+strconv.FormatBool(result), text, signature) }