我想要将代码从Java迁移到Go,这些方法应该能够对彼此的输出进行加密/解密,但是它们产生不同的结果,并且不能解密其他的密码:
java代码
public static byte[] encrypt(byte[] data, PublicKey publicKeyObject)
throws BadPaddingException, IllegalBlockSizeException,
InvalidKeyException, NoSuchPaddingException,
NoSuchAlgorithmException {
Cipher cipher = Cipher
.getInstance("RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING");
OAEPParameterSpec oaepParameterSpec = new OAEPParameterSpec("SHA-256",
"MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT);
try {
cipher.init(Cipher.ENCRYPT_MODE, publicKeyObject,
oaepParameterSpec);
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
return null;
}
return cipher.doFinal(data);
}
private static byte[] decrypt(byte[] data, PrivateKey privateKeyObj)
throws NoSuchPaddingException, NoSuchAlgorithmException,
InvalidKeyException, BadPaddingException,
IllegalBlockSizeException {
Cipher cipher = Cipher
.getInstance("RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING");
OAEPParameterSpec oaepParameterSpec = new OAEPParameterSpec("SHA-256",
"MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT);
try {
cipher.init(Cipher.DECRYPT_MODE, privateKeyObj, oaepParameterSpec);
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
return null;
}
return cipher.doFinal(data);
}
Go代码
rng := rand.Reader
ciphertext, err := rsa.EncryptOAEP(sha256.New(), rng, rsaPublicKey, secretMessage, label)
if err != nil {
fmt.Printf("Error from encryption: %s\n", err)
return
}
clearText, err := rsa.DecryptOAEP(sha256.New(), rng, rsaPrivateKey, ciphertext, label)
if err != nil {
fmt.Printf("Error from decryption: %s\n", err)
return
}
我甚至尝试过sha1作为Go的第一个参数,但结果是不同的。
有人能帮我吗?
发布于 2021-09-26 10:25:48
注释已经解释了这两种代码不兼容的原因,而其他答案:Go的crypto/rsa包与Java代码不同,不允许单独指定OAEP摘要和MGF1摘要,因此在这两种代码中都使用了不同的MGF1摘要。
相反,这个答案应该集中在修改crypto/rsa包来解决这个问题上。
这两个摘要的含义在RFC 8017中描述,更确切地说是在https://datatracker.ietf.org/doc/html/rfc8017#section-7.1部分,其中OAEP是定义的。
作为选项和输入参数,摘要(OAEP摘要)、掩码生成函数、标签、消息和公钥被指定为s. https://datatracker.ietf.org/doc/html/rfc8017#section-7.1.1。OAEP摘要用于散列标签s. https://datatracker.ietf.org/doc/html/rfc8017#section-7.1.1。作为掩码生成函数,RFC 8017只定义MGF1 (s. https://datatracker.ietf.org/doc/html/rfc8017#appendix-B.2.1),因此它通常用于OAEP。MGF1基于摘要(MGF1摘要)。
RFC 8017指定以下默认值: s. https://datatracker.ietf.org/doc/html/rfc8017#appendix-A.2.1:MGF1、OAEP和MGF1摘要的SHA1和空标签。
尽管SHA-1现在被认为是不安全的,但在OAEP,s. 这里的上下文中并不存在已知的不安全性。然而,SHA256经常被使用,作为一种预防措施或在从生态系统中消除SHA-1的过程中。
此外,RFC 8017并不排除对OAEP和MGF1摘要使用不同的摘要,就像在您的示例中一样。
因此,实现应该允许这两个摘要的独立规范,而crypto/rsa包没有做到这一点。
为了允许单独指定这两个摘要,必须在EncryptOAEP()
和DecryptOAEP()
函数中使用第二个参数来传递MGF1摘要,然后将该摘要应用于MGF1:
func EncryptOAEP(hash hash.Hash, hashMGF1 hash.Hash, random io.Reader, pub *rsa.PublicKey, msg []byte, label []byte) ([]byte, error) {
...
hashMGF1.Reset()
mgf1XOR(seed, hashMGF1, db)
mgf1XOR(db, hashMGF1, seed)
...
}
类似于DecryptOAEP()
。
这最好通过相应地调整crypto/rsa包本身来实现。或者,作为一种解决办法,可以从crypto/rsa包中复制所需的函数并对其进行调整,如下面的代码包含性测试所示:
package main
import (
"crypto/rand"
"crypto/subtle"
"crypto/rsa"
"crypto/sha256"
"crypto/sha1"
"crypto/x509"
"encoding/pem"
"encoding/base64"
"hash"
"errors"
"io"
"math/big"
"sync"
"fmt"
)
func main() {
var publicKeyData = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoZ67dtUTLxoXnNEzRBFB
mwukEJGC+y69cGgpNbtElQj3m4Aft/7cu9qYbTNguTSnCDt7uovZNb21u1vpZwKH
yVgFEGO4SA8RNnjhJt2D7z8RDMWX3saody7jo9TKlrPABLZGo2o8vadW8Dly/v+I
d0YDheCkVCoCEeUjQ8koXZhTwhYkGPu+vkdiqX5cUaiVTu1uzt591aO5Vw/hV4DI
hFKnOTnYXnpXiwRwtPyYoGTa64yWfi2t0bv99qz0BgDjQjD0civCe8LRXGGhyB1U
1aHjDDGEnulTYJyEqCzNGwBpzEHUjqIOXElFjt55AFGpCHAuyuoXoP3gQvoSj6RC
sQIDAQAB
-----END PUBLIC KEY-----`
var privateKeyData = `-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAoZ67dtUTLxoXnNEzRBFBmwukEJGC+y69cGgpNbtElQj3m4Af
t/7cu9qYbTNguTSnCDt7uovZNb21u1vpZwKHyVgFEGO4SA8RNnjhJt2D7z8RDMWX
3saody7jo9TKlrPABLZGo2o8vadW8Dly/v+Id0YDheCkVCoCEeUjQ8koXZhTwhYk
GPu+vkdiqX5cUaiVTu1uzt591aO5Vw/hV4DIhFKnOTnYXnpXiwRwtPyYoGTa64yW
fi2t0bv99qz0BgDjQjD0civCe8LRXGGhyB1U1aHjDDGEnulTYJyEqCzNGwBpzEHU
jqIOXElFjt55AFGpCHAuyuoXoP3gQvoSj6RCsQIDAQABAoIBAGoYl5ukuJk9Ga8a
LftLELRFaghuXXui7T0zQ4pASv9DCbiM3UWeCy1OjK1zAtXR2Kywz8JgN9DtnrVF
2uyCXr0wCPL/Y2P6cCRAKh2nYQrXbcvikpXt9311zH4qHGvdx/nP5oM0JHejuJCu
Re1btiwGTB3AoF+XzBAPSZ0gGl2FqDQ7qLqqwG9Xr+78STLdN8UOUCsKV3qdTM6N
XLeXliI0XIFQgT6XMiRGEvhJVaUTJ/3q23xza87k8jpqGsh5ArtnG6LUON26rEed
BL2ome7HNV+IOR143PXVrBMyn6qnwAas+Zt+WfCbBCP0k68oL7mzLmP6IzY4KBE9
BFEo04ECgYEA9GMgi2Xm9OqjUmihMt0oPnPcMx0DR+4mZezPVED2f3garOKcWvOV
y1N/Mn5A9L785jPjWE+ui7i5DT6AMJiWxkeEdYjXmZhpG9I3pha1yaLzBXjl+Dri
/dCXZxQq+Z7axnBxwIhDNHAeeCAau6hLfzsGgv5YAvSeg6KU7Af16dkCgYEAqUzG
jvZxfV/2qPMdNh9oUcvVbIcnIphnTP1Ma7BAD6anTnSru2EDLR66yiRtdrC9E54d
4xWeTNHsSUcaQBkAsyp7Cpewgy4vmo8GE3qUu91Jk3/1ZN6jxLyMoakyzhYTmq4s
QsTPC1daUXqpRjGYzP/8dMMzlKQ2Vncp+2BXgJkCgYEAinzJ6nSahluYpZBpGLu+
nHVnaQed3lsUI1oouyP9C4ryAtp/pAK49fmg8OoewRKhmYn54Qd2b/MD2n96gQ9X
EZFhfIFJO97kYUGlC1d/OH5AnO8/0oT8MLzNrzn8iGv+qcj6jRIqk0Kd4ZC/1Wuv
LLA0JnMfSL16PjoZjg+MyTECgYBRq47RooMnBycXY4hA9q+9XcZMP3qajsiudDbs
cC7HHg7xowjBMNB2cK+NGjuQGTxs/UbPqDsgNdh1lQ5Nw4H57FFEz94/ugUO21YE
CYs8gUigFgdMLLb2DjsNNXEjx7SXVtRVNVnnz7DrQ2/rQ7vBkO+5Z/03BGyOE5g2
AsjTaQKBgDLpbXN2p3eubQGJqv/K6f/9LBux/RWGXnZ+C1oCtGrUj+Ja8N6+cd6G
Mz9Go00GCdCUZXByx6rAZQaw7kWcI646miaplX4YtbX1d2mwbnmmz9EH4aRhzdby
9VDoPXBgf4dufgNoS3xP4NS4H5oPg0gPS0vwpWspWqplLM+N/kGj
-----END RSA PRIVATE KEY-----`
secretMessage := []byte("The quick brown fox jumps over the lazy dog")
label := []byte("")
rng := rand.Reader
// Encryption -------------------------------------------------------------
// Load public key
pubKeyBlock, _ := pem.Decode([]byte(publicKeyData))
var rsaPublicKey *rsa.PublicKey
pubInterface, parseErr := x509.ParsePKIXPublicKey(pubKeyBlock.Bytes)
if parseErr != nil {
fmt.Println("Load public key error")
panic(parseErr)
}
rsaPublicKey = pubInterface.(*rsa.PublicKey)
ciphertext, err := EncryptOAEP(sha256.New(), sha1.New(), rng, rsaPublicKey, secretMessage, label)
if err != nil {
fmt.Printf("Error from encryption: %s\n", err)
return
}
// Decryption -------------------------------------------------------------
// Load private key
privateKeyBlock, _ := pem.Decode([]byte(privateKeyData))
var rsaPrivateKey *rsa.PrivateKey
rsaPrivateKey, _ = x509.ParsePKCS1PrivateKey(privateKeyBlock.Bytes)
decrypted, err := DecryptOAEP(sha256.New(), sha1.New(), rng, rsaPrivateKey, ciphertext, label)
if err != nil {
fmt.Printf("Error from decryption: %s\n", err)
return
}
fmt.Println("Go Encryption/Decryption : " + string(decrypted))
// Cross-platform test: ciphertext from Java
/*
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING");
OAEPParameterSpec oaepParameterSpec = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT);
cipher.init(Cipher.ENCRYPT_MODE, publicKeyObject, oaepParameterSpec);
String ciphertextB64 = Base64.getEncoder().encodeToString(cipher.doFinal(data));
*/
ciphertext,_ = base64.StdEncoding.DecodeString("cCrJasWOwVFrAQ8S+p7Cdn7OnCJn/FiCjZLzDkDISOSv15u1HcLbVAqNa7ory2AW/tsV5tNz5Y53azs6SN7dwYlu58YH7kwqkwfmvUwK8pLdPPRXGaUy8/gEbM4wkwHUuxbYm/bpoEjpmICBtWzb5VOsE1RWHnZu1G2BqGKe1+sE1XadVKQpBqNSahYdthY2Dk21i/PStO5S4eRrgW2nDdmxCs9UtV4MBU8BVYHYF0TYweA/udBoGTizSDjgmWn0RXYJruGvFMHWCRRlPnj+pcelatIfY4YKOHREYifKVkphkB7PT/JaVFyMZWzOtqzE13ZBWBwBmA/yCNLE/7krcg==")
decrypted, err = DecryptOAEP(sha256.New(), sha1.New(), rng, rsaPrivateKey, ciphertext, label)
if err != nil {
fmt.Printf("Error from decryption: %s\n", err)
return
}
fmt.Println("Cross platform decryption: " + string(decrypted))
}
// From rsa package - Encryption -------------------------------------------------------------
func EncryptOAEP(hash hash.Hash, hashMGF1 hash.Hash, random io.Reader, pub *rsa.PublicKey, msg []byte, label []byte) ([]byte, error) {
if err := checkPub(pub); err != nil {
return nil, err
}
hash.Reset()
k := pub.Size()
if len(msg) > k-2*hash.Size()-2 {
return nil, rsa.ErrMessageTooLong
}
hash.Write(label)
lHash := hash.Sum(nil)
hash.Reset()
em := make([]byte, k)
seed := em[1 : 1+hash.Size()]
db := em[1+hash.Size():]
copy(db[0:hash.Size()], lHash)
db[len(db)-len(msg)-1] = 1
copy(db[len(db)-len(msg):], msg)
_, err := io.ReadFull(random, seed)
if err != nil {
return nil, err
}
hashMGF1.Reset()
mgf1XOR(db, hashMGF1, seed)
mgf1XOR(seed, hashMGF1, db)
m := new(big.Int)
m.SetBytes(em)
c := encrypt(new(big.Int), pub, m)
out := make([]byte, k)
return c.FillBytes(out), nil
}
func encrypt(c *big.Int, pub *rsa.PublicKey, m *big.Int) *big.Int {
e := big.NewInt(int64(pub.E))
c.Exp(m, e, pub.N)
return c
}
// From rsa package - Decryption -------------------------------------------------------------
func DecryptOAEP(hash hash.Hash, hashMGF1 hash.Hash, random io.Reader, priv *rsa.PrivateKey, ciphertext []byte, label []byte) ([]byte, error) { // hashMGF1 hash.Hash added
if err := checkPub(&priv.PublicKey); err != nil {
return nil, err
}
k := priv.Size()
if len(ciphertext) > k ||
k < hash.Size()*2+2 {
return nil, rsa.ErrDecryption
}
c := new(big.Int).SetBytes(ciphertext)
m, err := decrypt(random, priv, c)
if err != nil {
return nil, err
}
hash.Write(label)
lHash := hash.Sum(nil)
hash.Reset()
// We probably leak the number of leading zeros.
// It's not clear that we can do anything about this.
em := m.FillBytes(make([]byte, k))
firstByteIsZero := subtle.ConstantTimeByteEq(em[0], 0)
seed := em[1 : hash.Size()+1]
db := em[hash.Size()+1:]
hashMGF1.Reset()
mgf1XOR(seed, hashMGF1, db) // apply hashMGF1
mgf1XOR(db, hashMGF1, seed) // apply hashMGF1
lHash2 := db[0:hash.Size()]
// We have to validate the plaintext in constant time in order to avoid
// attacks like: J. Manger. A Chosen Ciphertext Attack on RSA Optimal
// Asymmetric Encryption Padding (OAEP) as Standardized in PKCS #1
// v2.0. In J. Kilian, editor, Advances in Cryptology.
lHash2Good := subtle.ConstantTimeCompare(lHash, lHash2)
// The remainder of the plaintext must be zero or more 0x00, followed
// by 0x01, followed by the message.
// lookingForIndex: 1 iff we are still looking for the 0x01
// index: the offset of the first 0x01 byte
// invalid: 1 iff we saw a non-zero byte before the 0x01.
var lookingForIndex, index, invalid int
lookingForIndex = 1
rest := db[hash.Size():]
for i := 0; i < len(rest); i++ {
equals0 := subtle.ConstantTimeByteEq(rest[i], 0)
equals1 := subtle.ConstantTimeByteEq(rest[i], 1)
index = subtle.ConstantTimeSelect(lookingForIndex&equals1, i, index)
lookingForIndex = subtle.ConstantTimeSelect(equals1, 0, lookingForIndex)
invalid = subtle.ConstantTimeSelect(lookingForIndex&^equals0, 1, invalid)
}
if firstByteIsZero&lHash2Good&^invalid&^lookingForIndex != 1 {
return nil, rsa.ErrDecryption
}
return rest[index+1:], nil
}
var bigZero = big.NewInt(0)
var bigOne = big.NewInt(1)
func decrypt(random io.Reader, priv *rsa.PrivateKey, c *big.Int) (m *big.Int, err error) {
// TODO(agl): can we get away with reusing blinds?
if c.Cmp(priv.N) > 0 {
err = rsa.ErrDecryption
return
}
if priv.N.Sign() == 0 {
return nil, rsa.ErrDecryption
}
var ir *big.Int
if random != nil {
MaybeReadByte(random)
// Blinding enabled. Blinding involves multiplying c by r^e.
// Then the decryption operation performs (m^e * r^e)^d mod n
// which equals mr mod n. The factor of r can then be removed
// by multiplying by the multiplicative inverse of r.
var r *big.Int
ir = new(big.Int)
for {
r, err = rand.Int(random, priv.N)
if err != nil {
return
}
if r.Cmp(bigZero) == 0 {
r = bigOne
}
ok := ir.ModInverse(r, priv.N)
if ok != nil {
break
}
}
bigE := big.NewInt(int64(priv.E))
rpowe := new(big.Int).Exp(r, bigE, priv.N) // N != 0
cCopy := new(big.Int).Set(c)
cCopy.Mul(cCopy, rpowe)
cCopy.Mod(cCopy, priv.N)
c = cCopy
}
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)
}
return
}
var (
closedChanOnce sync.Once
closedChan chan struct{}
)
func MaybeReadByte(r io.Reader) { // from "crypto/internal/randutil"
closedChanOnce.Do(func() {
closedChan = make(chan struct{})
close(closedChan)
})
select {
case <-closedChan:
return
case <-closedChan:
var buf [1]byte
r.Read(buf[:])
}
}
// From rsa package - both -------------------------------------------------------------
func mgf1XOR(out []byte, hash hash.Hash, seed []byte) {
var counter [4]byte
var digest []byte
done := 0
for done < len(out) {
hash.Write(seed)
hash.Write(counter[0:4])
digest = hash.Sum(digest[:0])
hash.Reset()
for i := 0; i < len(digest) && done < len(out); i++ {
out[done] ^= digest[i]
done++
}
incCounter(&counter)
}
}
func checkPub(pub *rsa.PublicKey) error {
if pub.N == nil {
return errPublicModulus
}
if pub.E < 2 {
return errPublicExponentSmall
}
if pub.E > 1<<31-1 {
return errPublicExponentLarge
}
return nil
}
var (
errPublicModulus = errors.New("crypto/rsa: missing public modulus")
errPublicExponentSmall = errors.New("crypto/rsa: public exponent too small")
errPublicExponentLarge = errors.New("crypto/rsa: public exponent too large")
)
func incCounter(c *[4]byte) {
if c[3]++; c[3] != 0 {
return
}
if c[2]++; c[2] != 0 {
return
}
if c[1]++; c[1] != 0 {
return
}
c[0]++
}
发布于 2021-09-22 15:25:30
我不是为了测试Go而设置的,但与OAEP的一个常见的不兼容性是推断MGF哈希在未指定时与“主”哈希相同,而不是默认为SHA-1。
换句话说,有两种哈希算法在使用中,它们可以独立选择,但Go API可能不会给您这种灵活性。如果您选择SHA-1,也许它对这两个函数都使用SHA-1,如果选择SHA-256,则这两个函数都使用它,而Java代码同时使用这两个函数。
作为调试策略,您可以在每个平台上对参数进行编码,并检查数据以确定参数中的任何差异。同样,我不熟悉Go API,但最好对加密期间使用的OAEP参数进行编码,并将它们与消息一起发送,这样收件人就可以解码正确的参数而不是猜测。
使用CMS或同等标准将为您提供一个标准格式来传输此信息,以及向收件人发送关键标识符和其他重要信息。
https://stackoverflow.com/questions/69286881
复制相似问题