![](/img/trans.png)
[英]How to encrypt and decrypt data with Openssl RSA public private key pair in Golang?
[英]Encrypt message with RSA private key (as in OpenSSL's RSA_private_encrypt)
我正在尝试在Go中实现Chef API客户端 ,但是在尝试创建正确的请求标头RSA签名时遇到了麻烦。 根据文件 :
规范标头由发送请求的客户端计算机使用的私钥签名,并且也使用Base64进行编码。
以下对ruby的OpenSSL::PKey::RSA.private_encrypt()
调用可以在mixlib-authentication
gem代码中找到 ,它使用OpenSSL绑定 , private_encrypt()
方法调用RSA_private_encrypt
openssl函数 。
不幸的是,我在Go的标准库中找不到匹配的函数。 crypto/rsa
看起来接近,但只实现了传统加密方法:加密与公钥与私钥散列签名。 OpenSSL的RSA_private_encrypt
相反的操作:它使用私钥加密(小的)消息(类似于从消息哈希创建签名)。
此“签名”也可以通过以下命令来实现:
openssl rsautl -sign -inkey path/to/private/key.pem \
-in file/to/encrypt -out encrypted/output
是否有任何本机Go库可以达到与OpenSSL的RSA_private_encrypt
相同的结果,或者唯一的方法是使用Cgo从OpenSSL库调用此函数? 也许我想念一些东西。 我的想法是实现没有任何非依赖项的客户端。
我是Go的新手,所以不确定我可以深入研究crypto/rsa
模块源。
找到了类似的问题 ,但是使用SignPKCS1v15
的答案显然是错误的(此函数加密消息的哈希,而不是消息本身 )。
在golang社区的大力帮助下 ,找到了解决方案:
Alex将原始代码发布在http://play.golang.org/p/jrqN2KnUEM上 (请参阅邮件列表 )。
我添加了rfc2313的第8节中指定的输入块大小检查: http : //play.golang.org/p/dGTl9siO8E
这是代码:
package main
import (
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"math/big"
"os/exec"
)
var (
ErrInputSize = errors.New("input size too large")
ErrEncryption = errors.New("encryption error")
)
func PrivateEncrypt(priv *rsa.PrivateKey, data []byte) (enc []byte, err error) {
k := (priv.N.BitLen() + 7) / 8
tLen := len(data)
// rfc2313, section 8:
// The length of the data D shall not be more than k-11 octets
if tLen > k-11 {
err = ErrInputSize
return
}
em := make([]byte, k)
em[1] = 1
for i := 2; i < k-tLen-1; i++ {
em[i] = 0xff
}
copy(em[k-tLen:k], data)
c := new(big.Int).SetBytes(em)
if c.Cmp(priv.N) > 0 {
err = ErrEncryption
return
}
var m *big.Int
var ir *big.Int
if priv.Precomputed.Dp == nil {
m = new(big.Int).Exp(c, priv.D, priv.N)
} else {
// We have the precalculated values needed for the CRT.
m = new(big.Int).Exp(c, priv.Precomputed.Dp, priv.Primes[0])
m2 := new(big.Int).Exp(c, priv.Precomputed.Dq, priv.Primes[1])
m.Sub(m, m2)
if m.Sign() < 0 {
m.Add(m, priv.Primes[0])
}
m.Mul(m, priv.Precomputed.Qinv)
m.Mod(m, priv.Primes[0])
m.Mul(m, priv.Primes[1])
m.Add(m, m2)
for i, values := range priv.Precomputed.CRTValues {
prime := priv.Primes[2+i]
m2.Exp(c, values.Exp, prime)
m2.Sub(m2, m)
m2.Mul(m2, values.Coeff)
m2.Mod(m2, prime)
if m2.Sign() < 0 {
m2.Add(m2, prime)
}
m2.Mul(m2, values.R)
m.Add(m, m2)
}
}
if ir != nil {
// Unblind.
m.Mul(m, ir)
m.Mod(m, priv.N)
}
enc = m.Bytes()
return
}
func main() {
// o is output from openssl
o, _ := exec.Command("openssl", "rsautl", "-sign", "-inkey", "t.key", "-in", "in.txt").Output()
// t.key is private keyfile
// in.txt is what to encode
kt, _ := ioutil.ReadFile("t.key")
e, _ := ioutil.ReadFile("in.txt")
block, _ := pem.Decode(kt)
privkey, _ := x509.ParsePKCS1PrivateKey(block.Bytes)
encData, _ := PrivateEncrypt(privkey, e)
fmt.Println(encData)
fmt.Println(o)
fmt.Println(string(o) == string(encData))
}
更新:我们可以期望在Go 1.3中对此种签名具有本地支持,请参见适当的commit 。
从版本1.3
,您可以使用SignPKCS1v15
轻松完成此操作
rsa.SignPKCS1v15(nil, priv, crypto.Hash(0), signedData)
请参阅: https : //groups.google.com/forum/#!topic/Golang-Nuts/Vocj33WNhJQ
我在这个问题上停留了一段时间。
最终,我在这里用代码来讨价还价: https : //github.com/bitmartexchange/bitmart-go-api/blob/master/bm_client.go
// Sign secret with rsa with PKCS 1.5 as the padding algorithm
// The result should be exactly same as "openssl rsautl -sign -inkey "YOUR_RSA_PRIVATE_KEY" -in "YOUR_PLAIN_TEXT""
signer, err := rsa.SignPKCS1v15(rand.Reader, rsaPrivateKey.(*rsa.PrivateKey), crypto.Hash(0), []byte(message))
欢迎来到openssl的乐趣...这是一个难以置信的名字不好的函数。 如果您在Ruby代码中四处寻找,则将调用openssl函数
http://www.openssl.org/docs/crypto/RSA_private_encrypt.html
阅读文档,这实际上是使用私钥对缓冲区进行签名,而不是对其进行加密。
描述
这些功能在较低级别处理RSA签名。
RSA_private_encrypt()使用私钥rsa从(通常是带有算法标识符的消息摘要)处的flen字节签名,并将签名存储在其中。 必须指向内存的RSA_size(rsa)字节。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.