Golang與非對稱加密

1、非對稱加密介紹
非對稱加密和對稱加密不同,主要區別如下
-
使用公鑰加密,使用私鑰解密
-
公鑰和私鑰不同
-
公鑰可以公佈給所有人
-
私鑰只有自己儲存
-
相比於對稱加密,運算速度非常慢
加密過程:明文+公鑰——>密文
解密過程:密文+私鑰——>明文
非對稱加密演算法常用於資料加密和身份認證, 常見的非對稱加密演算法如下
DSS DSA
2、DSA
DSA
是基於整數有限域離散對數難題的,其安全性與 RSA
相比差不多。 DSA
的一個重要特點是兩個素數公開,這樣,當使用別人的 p
和 q
時,即使不知道私鑰,你也能確認它們是否是隨機產生的,還是作了手腳。 RSA
演算法卻做不到,但是其缺點就是隻能用於數字簽名,不能用於加密
3、RSA
在 1976
年,由於對稱加密演算法已經不能滿足需要, Diffie
和 Hellman
發表了一篇叫《密碼學新動向》的文章,介紹了公匙加密的概念,由 Rivet
、 Shamir
、 Adelman
提出了 RSA
演算法
RSA
是目前最有影響力的公鑰加密演算法,它能夠抵抗到目前為止已知的絕大多數密碼攻擊,已被 ISO
推薦為公鑰資料加密標準
命名:Ron Rivest、Adi Shamir、Leonard Adleman
- 金鑰越長,越難破解,目前
768
位的金鑰還無法破解(至少沒人公開宣佈),因此可以認為1024
位的RSA
金鑰基本安全,2048
位的金鑰極其安全 -
RSA
的演算法原理主要用到了數論
3.1 RSA的加密過程
1、隨機選擇兩個不相等的質數 p
和 q
,p=61,q=53
2、計算 p
和 q
的乘積,n=3233
3、計算 n
的尤拉函式∅(n) = (p-1)(q-1),∅(n)=3120
4、隨機選擇一個整數 e
,使得1<e<∅(n),且 e
與 ∅(n)
互質,e=17
5、計算 e
對於 ∅(n)
的模反元素d,即求解e*d + ∅(n)*y =1,d=2753,y=-15
6、將 n
和 e
封裝成公鑰, n
和 d
封裝成私鑰,公鑰=(3233, 17),私鑰=(3233, 2753)
3.2 呼叫示例
RSA
使用示例程式碼
package main import ( "crypto/rand" "crypto/rsa" "crypto/sha1" "crypto/x509" "encoding/pem" "fmt" ) // 使用對方的公鑰的資料, 只有對方的私鑰才能解開 func encrypt(plain string, publicKey string) (cipherByte []byte, err error) { msg := []byte(plain) // 解碼公鑰 pubBlock, _ := pem.Decode([]byte(publicKey)) // 讀取公鑰 pubKeyValue, err := x509.ParsePKIXPublicKey(pubBlock.Bytes) if err != nil { panic(err) } pub := pubKeyValue.(*rsa.PublicKey) // 加密資料方法: 不用使用EncryptPKCS1v15方法加密,原始碼裡面推薦使用EncryptOAEP, 因此這裡使用安全的方法加密 encryptOAEP, err := rsa.EncryptOAEP(sha1.New(), rand.Reader, pub, msg, nil) if err != nil { panic(err) } cipherByte = encryptOAEP return } // 使用私鑰解密公鑰加密的資料 func decrypt(cipherByte []byte, privateKey string) (plainText string, err error) { // 解析出私鑰 priBlock, _ := pem.Decode([]byte(privateKey)) priKey, err := x509.ParsePKCS1PrivateKey(priBlock.Bytes) if err != nil { panic(err) } // 解密RSA-OAEP方式加密後的內容 decryptOAEP, err := rsa.DecryptOAEP(sha1.New(), rand.Reader, priKey, cipherByte, nil) if err != nil { panic(err) } plainText = string(decryptOAEP) return } func test() { msg := "Content bo be encrypted!" // 獲取公鑰, 生產環境往往是檔案中讀取, 這裡為了測試方便, 直接生成了. publicKeyData := `-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDZsfv1qscqYdy4vY+P4e3cAtmv ppXQcRvrF1cB4drkv0haU24Y7m5qYtT52Kr539RdbKKdLAM6s20lWy7+5C0Dgacd wYWd/7PeCELyEipZJL07Vro7Ate8Bfjya+wltGK9+XNUIHiumUKULW4KDx21+1NL AUeJ6PeW+DAkmJWF6QIDAQAB -----END PUBLIC KEY----- ` // 獲取私鑰 privateKeyData := `-----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQDZsfv1qscqYdy4vY+P4e3cAtmvppXQcRvrF1cB4drkv0haU24Y 7m5qYtT52Kr539RdbKKdLAM6s20lWy7+5C0DgacdwYWd/7PeCELyEipZJL07Vro7 Ate8Bfjya+wltGK9+XNUIHiumUKULW4KDx21+1NLAUeJ6PeW+DAkmJWF6QIDAQAB AoGBAJlNxenTQj6OfCl9FMR2jlMJjtMrtQT9InQEE7m3m7bLHeC+MCJOhmNVBjaM ZpthDORdxIZ6oCuOf6Z2+Dl35lntGFh5J7S34UP2BWzF1IyyQfySCNexGNHKT1G1 XKQtHmtc2gWWthEg+S6ciIyw2IGrrP2Rke81vYHExPrexf0hAkEA9Izb0MiYsMCB /jemLJB0Lb3Y/B8xjGjQFFBQT7bmwBVjvZWZVpnMnXi9sWGdgUpxsCuAIROXjZ40 IRZ2C9EouwJBAOPjPvV8Sgw4vaseOqlJvSq/C/pIFx6RVznDGlc8bRg7SgTPpjHG 4G+M3mVgpCX1a/EU1mB+fhiJ2LAZ/pTtY6sCQGaW9NwIWu3DRIVGCSMm0mYh/3X9 DAcwLSJoctiODQ1Fq9rreDE5QfpJnaJdJfsIJNtX1F+L3YceeBXtW0Ynz2MCQBI8 9KP274Is5FkWkUFNKnuKUK4WKOuEXEO+LpR+vIhs7k6WQ8nGDd4/mujoJBr5mkrw DPwqA3N5TMNDQVGv8gMCQQCaKGJgWYgvo3/milFfImbp+m7/Y3vCptarldXrYQWO AQjxwc71ZGBFDITYvdgJM1MTqc8xQek1FXn1vfpy2c6O -----END RSA PRIVATE KEY----- ` cipherData, err := encrypt(msg, publicKeyData) if err != nil { panic(err) } fmt.Printf("encrypt message: %x\n", cipherData) plainData, err := decrypt(cipherData, privateKeyData) if err != nil { panic(err) } fmt.Printf("decrypt message:%s\n", plainData) } func main() { test() }
4、ECC
ECC
又稱橢圓曲線加密
ECC
(Elliptic Curve Cryptography)橢圓曲線加密演算法,相比 RSA
, ECC
可以使用更短的金鑰,來實現與 RSA
相當或更高的安全
定義了橢圓曲線上的加法和二倍運算
橢圓曲線依賴的數學難題是: k
為正整數, p
是橢圓曲線上的點(稱為基點),k*p=Q,已知 Q
和 P
,很難計算出k
ECC
是建立在基於橢圓曲線的離散對數的難度, 大概過程如下
給定橢圓曲線上的一個點P,一個整數k,求解Q=kP很容易;給定一個點P、Q,知道Q=kP,求整數k確是一個難題。ECDH即建立在此數學難題之上
今天只有短的 RSA
鑰匙才可能被強力方式解破。到 2008
年為止,世界上還沒有任何可靠的攻擊RSA演算法的方式。只要其鑰匙的長度足夠長,用 RSA
加密的資訊實際上是不能被解破的。但在分散式計算和量子計算機理論日趨成熟的今天, RSA
加密安全性受到了挑戰
隨著分解大整數方法的進步及完善、計算機速度的提高以及計算機網路的發展,為了保障資料的安全, RSA
的金鑰需要不斷增加,但是,金鑰長度的增加導致了其加解密的速度大為降低,硬體實現也變得越來越難以忍受,這對使用 RSA
的應用帶來了很重的負擔,因此需要一種新的演算法來代替 RSA
1985
年 N.Koblitz
和 Miller
提出將橢圓曲線用於密碼演算法,根據是有限域上的橢圓曲線上的點群中的離散對數問題 ECDLP
。 ECDLP
是比因子分解問題更難的問題,它是指數級的難度
橢圓曲線演算法因引數不同有多種型別, 這個網站列出了現階段那些 ECC
是相對安全的:橢圓曲線演算法安全列表, 而 curve25519
便是其中的佼佼者
Curve25519/Ed25519/X25519
是著名密碼學家 Daniel J. Bernstein
在 2006
年獨立設計的橢圓曲線加密/簽名/金鑰交換演算法, 和現有的任何橢圓曲線演算法都完全獨立
特點是:
- 完全開放設計: 演算法各引數的選擇直截了當,非常明確,沒有任何可疑之處,相比之下目前廣泛使用的橢圓曲線是NIST系列標準,方程的係數是使用來歷不明的隨機種子 c49d3608 86e70493 6a6678e1 139d26b7 819f7e90 生成的,非常可疑,疑似後門;
- 高安全性: 一個橢圓曲線加密演算法就算在數學上是安全的,在實用上也並不一定安全,有很大的概率通過快取、時間、惡意輸入摧毀安全性,而25519系列橢圓曲線經過特別設計,儘可能的將出錯的概率降到了最低,可以說是實踐上最安全的加密演算法。例如,任何一個32位隨機數都是一個合法的X25519公鑰,因此通過惡意數值攻擊是不可能的,演算法在設計的時候刻意避免的某些分支操作,這樣在程式設計的時候可以不使用if ,減少了不同if分支程式碼執行時間不同的時序攻擊概率,相反, NIST系列橢圓曲線演算法在實際應用中出錯的可能性非常大,而且對於某些理論攻擊的免疫能力不高, Bernstein 對市面上所有的加密演算法使用12個標準進行了考察, 25519是幾乎唯一滿足這些標準的;
- 速度快: 25519系列曲線是目前最快的橢圓曲線加密演算法,效能遠遠超過NIST系列,而且具有比P-256更高的安全性;
- 作者功底深厚: Daniel J. Bernstein是世界著名的密碼學家,他在大學曾經開設過一門 UNIX 系統安全的課程給學生,結果一學期下來,發現了 UNIX 程式中的 91 個安全漏洞;他早年在美國依然禁止出口加密演算法時,曾因為把自己設計的加密演算法釋出到網上遭到了美國政府的起訴,他本人抗爭六年,最後美國政府撤銷所有指控,目前另一個非常火的高效能安全流密碼 ChaCha20 也是出自 Bernstein 之手;
- 下一代的標準: 25519系列曲線自2006年發表以來,除了學術界無人問津, 2013 年愛德華·斯諾登曝光稜鏡計劃後,該演算法突然大火,大量軟體,如OpenSSH都迅速增加了對25519系列的支援,如今25519已經是大勢所趨,可疑的NIST曲線遲早要退出橢圓曲線的歷史舞臺,目前, RFC增加了SSL/TLS對X25519金鑰交換協議的支援,OpenSSL 1.1也加入支援,是擺脫老大哥的第一步,下一步是將 Ed25519做為可選的TLS證書籤名演算法,徹底擺脫NIST
5、ECC與RSA的比較
ECC
和 RSA
相比,在許多方面都有對絕對的優勢,主要體現在以下方面:
- 抗攻擊性強。相同的金鑰長度,其抗攻擊性要強很多倍
- 計算量小,處理速度快。
ECC
總的速度比RSA
、DSA
要快得多 - 儲存空間佔用小。
ECC
的金鑰尺寸和系統引數與RSA
、DSA
相比要小得多,意味著它所佔的存貯空間要小得多。這對於加密演算法在IC
卡上的應用具有特別重要的意義 - 頻寬要求低。當對長訊息進行加解密時,三類密碼系統有相同的頻寬要求,但應用於短訊息時
ECC
頻寬要求卻低得多。頻寬要求低使ECC
在無線網路領域具有廣泛的應用前景
ECC
的這些特點使它必將取代 RSA
,成為通用的公鑰加密演算法。比如 SET
協議的制定者已把它作為下一代 SET
協議中預設的公鑰密碼演算法
6、ECDSA
因為在數字簽名的安全性高, 基於 ECC
的 DSA
更高, 所以非常適合數字簽名使用場景, 在 SSH TLS
有廣泛使用, ECC
把離散對數安全性高很少,所以 ECC
在安全領域會成為下一個標準
在 golang
的 ssh
庫中就是使用這個演算法來簽名的: A
使用自己的私鑰簽名一段資料,然後將公鑰發放出去。使用者拿到公鑰後,驗證資料的簽名,如果通過則證明資料來源是 A
,從而達到身份認證的作用
package main import ( "crypto/ecdsa" "crypto/elliptic" "crypto/md5" "crypto/rand" "fmt" "hash" "io" "math/big" ) // SignData 用於儲存簽名的資料 type SignData struct { r *big.Int s *big.Int signhash *[]byte signature *[]byte } // 使用私鑰簽名一段資料 func sign(message string, privateKey *ecdsa.PrivateKey) (signData *SignData, err error) { // 簽名資料 var h hash.Hash h = md5.New() r := big.NewInt(0) s := big.NewInt(0) io.WriteString(h, message) signhash := h.Sum(nil) r, s, serr := ecdsa.Sign(rand.Reader, privateKey, signhash) if serr != nil { return nil, serr } signature := r.Bytes() signature = append(signature, s.Bytes()...) signData = &SignData{ r: r, s: s, signhash: &signhash, signature: &signature, } return } // 校驗數字簽名 func verifySign(signData *SignData, publicKey *ecdsa.PublicKey) (status bool) { status = ecdsa.Verify(publicKey, *signData.signhash, signData.r, signData.s) return } func test() { //使用橢圓曲線的P256演算法,現在一共也就實現了4種,我們使用折中一種,具體見http://golang.org/pkg/crypto/elliptic/#P256 pubkeyCurve := elliptic.P256() privateKey := new(ecdsa.PrivateKey) // 生成祕鑰對 privateKey, err := ecdsa.GenerateKey(pubkeyCurve, rand.Reader) if err != nil { panic(err) } var publicKey ecdsa.PublicKey publicKey = privateKey.PublicKey // 簽名 signData, err := sign("This is a message to be signed and verified by ECDSA!", privateKey) if err != nil { panic(err) } fmt.Printf("The signhash: %x\nThe signature: %x\n", *signData.signhash, *signData.signature) // 驗證 status := verifySign(signData, &publicKey) fmt.Printf("The verify result is: %v\n", status) } func main() { test() }
See you ~
- Gradle打包工具入門
- 服務網格和Istio初識-續
- 服務網格和Istio初識
- Golang與非對稱加密
- ack叢集Terway網路場景下的vSwitch擴容
- Golang與對稱加密
- 基於ack k8s叢集排程的方案設計
- 基於Dockerfile構建容器映象的最佳實踐
- Golang反射-下篇
- Golang反射-上篇
- Azure DevOps的使用入門
- Golang介面型別-下篇
- Golang介面型別-上篇
- 基於Python實現原生的登入驗證碼
- Golang開發命令列工具之flag包的使用
- Golang檔案操作-下篇
- k8s環境下處理容器時間問題的多種姿勢
- Golang基準測試
- 淺談Prometheus的資料儲存
- Golang單元測試