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.
CodePudding user response:
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
}