8

Golang RSA 如何私钥加密公钥解密

 3 years ago
source link: https://ttys3.dev/post/go-rsa-private-encrypt-and-public-decrypt/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

March 22, 2021

一般来说,加密主要用于消息的传递,并且传递的消息只有持有私钥的那个人能解密。

因此, 像 RSA 这种非对称密钥加密算法的常用场景是:

对于加密,公钥加密,私钥解密。

为什么不是 私钥加密,公钥解密呢? 因为使用场景是假定 公钥 都可以获取到,因而公钥解密这种操作也就与明文无异了。

有没有私钥加密的常用场景呢?当然是有,比如常见的签名操作。

明文消息 -> 进行 hash -> 对 hash 后的结果进行私钥加密, 这个加密后的结果就是签名了。

对签名进行验证实际上就是用公钥解密,然后把解密后的hash与原始消息的hash进行对比。

客户端:明文消息 -> 进行 hash -> 对签名使用公钥进行解密,并对比 hash 结果。

the story

早期版本的 Go 标准库 并不提供私钥加密 和 公钥解密功能。但是大家应该能猜想到,

其实要实现 私钥加密, 只需要在原来的签名函数上修改一个 hash 参数,使得这个 hash 啥也不做。

当然,由于明文并不能像 hash 一样是固定位数 的,因此,这里自然免不了要 padding.

Go 的 签名是采用的 PKCS #1 v1.5 padding 方式。

这个提交使得标准库直接支持 私钥加密了:

https://github.com/golang/go/commit/78c16c9b16dc9c64d1ddad6db5afaab12e87e8f2#diff-fd7abb32dfad4ffb52bb17669ce4a8a4114917e7d4a055107a9f1fc9381f1c94R233

crypto/rsa: support unpadded signatures.

Usually when a message is signed it’s first hashed because RSA has low limits on the size of messages that it can sign. However, some protocols sign short messages directly. This isn’t a great idea because the messages that can be signed suddenly depend on the size of the RSA key, but several people on golang-nuts have requested support for this and it’s very easy to do.

R=golang-codereviews, rsc CC=golang-codereviews golang.org/cl/44400043

the solution

Since go 1.3, you can easily do this using SignPKCS1v15

rsa.SignPKCS1v15(nil, priv, crypto.Hash(0), plainData) 

refer:  https://groups.google.com/forum/#!topic/Golang-Nuts/Vocj33WNhJQ

所以,现在的 SignPKCS1v15 方法是这样的:

// SignPKCS1v15 calculates the signature of hashed using
// RSASSA-PKCS1-V1_5-SIGN from RSA PKCS #1 v1.5.  Note that hashed must
// be the result of hashing the input message using the given hash
// function. If hash is zero, hashed is signed directly. This isn't
// advisable except for interoperability.
//
// If rand is not nil then RSA blinding will be used to avoid timing
// side-channel attacks.
//
// This function is deterministic. Thus, if the set of possible
// messages is small, an attacker may be able to build a map from
// messages to signatures and identify the signed messages. As ever,
// signatures provide authenticity, not confidentiality.
func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) {
	hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed))
	if err != nil {
		return nil, err
	}

	tLen := len(prefix) + hashLen
	k := priv.Size()
	if k < tLen+11 {
		return nil, ErrMessageTooLong
	}

	// EM = 0x00 || 0x01 || PS || 0x00 || T
	em := make([]byte, k)
	em[1] = 1
	for i := 2; i < k-tLen-1; i++ {
		em[i] = 0xff
	}
	copy(em[k-tLen:k-hashLen], prefix)
	copy(em[k-hashLen:k], hashed)

	m := new(big.Int).SetBytes(em)
	c, err := decryptAndCheck(rand, priv, m)
	if err != nil {
		return nil, err
	}

	return c.FillBytes(em), nil
}

如果给 rand 传 nil 则每次生成的加密结果都是一样的。

如果给 crypto.Hash() 传0, 则表示不进行任何 hash

因此,私钥加密实际上只是一个特殊参数的调用:

encrypted, err := rsa.SignPKCS1v15(nil, priv, crypto.Hash(0), []byte(message))

troubleshoot

Golang 加密后的消息给 node.js 解密时,node.js 报错:

rsa routines:RSA_padding_check_PKCS1_type_1:block type is not 01

不要被错误信息迷惑了。这种错误跟 block type (padding) 没有什么关系。

错误的根本原因是,要么是加密的人私钥用错了,要么是解密的人公钥用错了。(没错,我在写某知名软件的注册机时,因为这个错误提示浪费了很多时间 ,直到后面意识到了问题的真正原因。 另外,我还要在这里吐槽一下某公司,你们的注册算法写得太烂了,有标准的签名算法不用,直接用的私钥加密/公钥解密这一套。还害我写这么一篇文章 。另外,都2021年了,你们还在用 RSA 1024)

refs

https://golang.org/src/crypto/rsa/pkcs1v15.go?s=8774:8874#L221

https://gist.github.com/kkHAIKE/be3b8d7ff8886457c6fdac2714d56fe1

https://github.com/buf1024/golib/blob/master/crypt/rsa.go

https://topic.alibabacloud.com/a/golang-private-key-encrypt-public-key-decrypt_1_38_30920103.html

node.js 类似 https://www.geeksforgeeks.org/node-js-crypto-publicdecrypt-method/

https://github.com/nodejs/help/issues/1093


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK