无法在Golang中验证RSA PSS分离签名,尽管OpenSSL成功。

huangapple go评论79阅读模式
英文:

Can't verify RSA PSS detached signature in Golang despite OpenSSL success

问题

我尽力而为了。我阅读了我能找到的每个Go库,试图找出可能出错的地方,但是我失败了。

OpenSSL签名命令:

openssl cms -sign -binary -md sha256 -in Thing.tar -outform der -out Thing.sig -signer thing-sign.pem -keyopt rsa_padding_mode:pss

我已经提供了正确的OpenSSL指令:

openssl cms -verify -binary -md sha256 -in Thing.sig -inform DER -content Thing.tar -out Thing.dmp -CAfile ca-chain.pem

OpenSSL成功了,但是我在Golang中的每一次尝试都是惨败。我认为这可能与填充有关,但这只是一个不太准确的猜测。这就是我以为我理解了这个过程的结果!我真傻。

如果需要,我很乐意提供已经尝试过的文档,只是我此刻没有耐心。如果有人能提供关于如何复制这个OpenSSL命令的见解,我将非常感激。

让我知道我可以提供什么来帮助。

谢谢!

编辑

现在我休息好了,这是我目前的代码:

func(t *Thing) VerifyThisThingWithSig() (bool, error) {

    sig, err := ioutil.ReadFile(t.sigPath)
    if err != nil {
        return false, err
    }

    rootPem, err := ioutil.ReadFile(cfg.Certs.CACertChain)
    if err != nil {
        return false, err
    }

    block, _ := pem.Decode(rootPem)

    var cert *x509.Certificate
    cert, _ = x509.ParseCertificate(block.Bytes)
    rsaPubKey := cert.PublicKey.(*rsa.PublicKey)

    hasher := sha256.New()
    f, err := os.Open(t.tarPath)
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
    if _, err := io.Copy(hasher, f); err != nil {
        log.Fatal(err)
    }

    var opts rsa.PSSOptions

    err = rsa.VerifyPSS(rsaPubKey, crypto.SHA256, hasher.Sum(nil), sig, &opts)
    if err != nil {
        return false, err
    }

    return true, err
}

查看go src中的rsa.VerifyPSS():

func VerifyPSS(pub *PublicKey, hash crypto.Hash, digest []byte, sig []byte, opts *PSSOptions) error {
	if len(sig) != pub.Size() {
		return ErrVerification
	}
	s := new(big.Int).SetBytes(sig)
	m := encrypt(new(big.Int), pub, s)
	emBits := pub.N.BitLen() - 1
	emLen := (emBits + 7) / 8
	if m.BitLen() > emLen*8 {
		return ErrVerification
	}
	em := m.FillBytes(make([]byte, emLen))
	return emsaPSSVerify(digest, em, emBits, opts.saltLength(), hash.New())
}

...我在公钥和签名的大小比较上就失败了(我的分别是256和1782)。

从ASN.1数据中读取,我有OID:

1.2.840.113549.1.1.10 rsaPSS (PKCS #1)
1.2.840.113549.1.1.8 pkcs1-MGF (PKCS #1)

是我如何读取签名的问题吗?使用pkcs7库,我可以轻松解析签名数据,但我无法用它来验证pkcs1 rsapss。我在加密领域迷失了。

编辑2

好的,我想我可以手动做到这一点。我认为我需要解析pkcs7签名,提取包含在创建时生成的内容的sha256摘要的OID,并与验证时生成的内容sha256进行比较。

除非有其他建议,否则我将继续这样做。我会报告结果。

英文:

I've tried everything I could think of. I read through every Go library I could find to figure out where I could have possibly gone wrong, but I lost it.

OpenSSL signing command:

openssl cms -sign -binary -md sha256 -in Thing.tar -outform der -out Thing.sig -signer thing-sign.pem -keyopt rsa_padding_mode:pss

I was provided proper OpenSSL instructions:

openssl cms -verify -binary -md sha256 -in Thing.sig -inform DER -content Thing.tar -out Thing.dmp -CAfile ca-chain.pem

OpenSSL is successful, but my every attempt in Golang is a miserable failure. I think it might have something to do with padding, but that is a less-than-educated guess at this point. This is what I get for thinking I understood the process! Silly me.

I'm happy to provide documentation on what has been tried so far if it's required, I just don't have the patience at this moment. If anyone can provide insight on how to replicate this OpenSSL command, I would be very grateful.

Let me know what I can provide to help.

Thank you!

Edit:

Now that I'm rested, here's the code I'm sitting on:

func(t *Thing) VerifyThisThingWithSig() (bool, error) {

    sig, err := ioutil.ReadFile(t.sigPath)
    if err != nil {
        return false, err
    }

    rootPem, err := ioutil.ReadFile(cfg.Certs.CACertChain)
    if err != nil {
        return false, err
    }

    block, _ := pem.Decode(rootPem)

    var cert *x509.Certificate
    cert, _ = x509.ParseCertificate(block.Bytes)
    rsaPubKey := cert.PublicKey.(*rsa.PublicKey)

    hasher := sha256.New()
    f, err := os.Open(t.tarPath)
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
    if _, err := io.Copy(hasher, f); err != nil {
        log.Fatal(err)
    }

    var opts rsa.PSSOptions

    err = rsa.VerifyPSS(rsaPubKey, crypto.SHA256, hasher.Sum(nil), sig, &opts)
    if err != nil {
        return false, err
    }

    return true, err
}

Looking at rsa.VerifyPSS() in go src:

func VerifyPSS(pub *PublicKey, hash crypto.Hash, digest []byte, sig []byte, opts *PSSOptions) error {
	if len(sig) != pub.Size() {
		return ErrVerification
	}
	s := new(big.Int).SetBytes(sig)
	m := encrypt(new(big.Int), pub, s)
	emBits := pub.N.BitLen() - 1
	emLen := (emBits + 7) / 8
	if m.BitLen() > emLen*8 {
		return ErrVerification
	}
	em := m.FillBytes(make([]byte, emLen))
	return emsaPSSVerify(digest, em, emBits, opts.saltLength(), hash.New())
}

...I fail right off the bat with the size comparison of the public key and signature (mine are 256 and 1782, respectively).

Reading from the ASN.1 data, I've got OIDs:

1.2.840.113549.1.1.10 rsaPSS (PKCS #1)
1.2.840.113549.1.1.8 pkcs1-MGF (PKCS #1)

Is it how I'm reading the sig? Using the pkcs7 library, I was able to parse the sig data no problem, but I can't use it to verify pkcs1 rsapss. I'm lost in the woods of crypto.

Edit 2:

Ok. I think I can do this manually. I think I need to parse the pkcs7 signature, pull the OID containing the sha256 sum of the detached content generated at creation, and compare against the content sha256 I generate at verification time.

That's where I'm headed next unless advised otherwise. Will report results.

答案1

得分: 0

好的,以下是翻译好的内容:

好的,我不得不重写/合并两个现有的库来完成这个任务。

一个库支持我需要的RSAPSS算法,但不支持证书链或签名时间验证。另一个库支持证书链和签名时间验证,但不支持RSAPSS算法。所以我将我需要的部分合并到一个新的库中,该库同时支持这两个功能。

最终的代码:

func (t *Thing) VerifyThingWithSig() (bool, error) {

    tar, err := ioutil.ReadFile(t.tarPath)
    if err != nil {
        return false, err
    }

    sig, err := ioutil.ReadFile(t.sigPath)
    if err != nil {
        return false, err
    }

    certPool := x509.NewCertPool()
    certs, err := ioutil.ReadFile(cfg.Certs.ThingCAChain)
    if err != nil {
        return false, err
    }
    certPool.AppendCertsFromPEM(certs)

    p7, err := pkcs7.Parse(sig)
    if err != nil {
        return false, err
    }

    p7.Content = tar

    if bytes.Compare(tar, p7.Content) != 0 {
        err = fmt.Errorf("Content was not in the parsed data")
        return false, err
    }

    if err = p7.VerifyWithChain(certPool); err != nil {
        return false, err
    }

    return true, err
}
英文:

OK, I had to rewrite/merge two existing libraries to do it, but the deed is done.

One library supported the RSAPSS I needed, but not cert chain or signing time verification. The other supported cert chain and signing time verification, but not the RSAPSS algorithm. So I merged the parts I needed into a new library that supports both.

Final code:

func(t *Thing) VerifyThingWithSig() (bool, error) {

    tar, err := ioutil.ReadFile(t.tarPath)
    if err != nil {
        return false, err
    }

    sig, err := ioutil.ReadFile(t.sigPath)
    if err != nil {
        return false, err
    }

    certPool := x509.NewCertPool()
    certs, err := ioutil.ReadFile(cfg.Certs.ThingCAChain)
    if err != nil {
        return false, err
    }
    certPool.AppendCertsFromPEM(certs)

    p7, err := pkcs7.Parse(sig)
    if err != nil {
        return false, err
    }

    p7.Content = tar

    if bytes.Compare(tar, p7.Content) != 0 {
        err = fmt.Errorf("Content was not in the parsed data")
        return false, err
    }

    if err = p7.VerifyWithChain(certPool); err != nil {
		return false, err
    }

    return true, err
}

huangapple
  • 本文由 发表于 2022年2月22日 15:17:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/71217283.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定