首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Java RSA/ECB/OAEPWITHSHA-256 ANDMGF1 Migrate To Go

Java RSA/ECB/OAEPWITHSHA-256 ANDMGF1 Migrate To Go
EN

Stack Overflow用户
提问于 2021-09-22 15:07:04
回答 2查看 993关注 0票数 4

我想要将代码从Java迁移到Go,这些方法应该能够对彼此的输出进行加密/解密,但是它们产生不同的结果,并且不能解密其他的密码:

java代码

代码语言:javascript
运行
复制
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代码

代码语言:javascript
运行
复制
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的第一个参数,但结果是不同的。

有人能帮我吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 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:

代码语言:javascript
运行
复制
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包中复制所需的函数并对其进行调整,如下面的代码包含性测试所示:

代码语言:javascript
运行
复制
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]++
}
票数 4
EN

Stack Overflow用户

发布于 2021-09-22 15:25:30

我不是为了测试Go而设置的,但与OAEP的一个常见的不兼容性是推断MGF哈希在未指定时与“主”哈希相同,而不是默认为SHA-1。

换句话说,有两种哈希算法在使用中,它们可以独立选择,但Go API可能不会给您这种灵活性。如果您选择SHA-1,也许它对这两个函数都使用SHA-1,如果选择SHA-256,则这两个函数都使用它,而Java代码同时使用这两个函数。

作为调试策略,您可以在每个平台上对参数进行编码,并检查数据以确定参数中的任何差异。同样,我不熟悉Go API,但最好对加密期间使用的OAEP参数进行编码,并将它们与消息一起发送,这样收件人就可以解码正确的参数而不是猜测。

使用CMS或同等标准将为您提供一个标准格式来传输此信息,以及向收件人发送关键标识符和其他重要信息。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69286881

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档