Added support for RSASSA-PSS and RSAES-OAEP.
This commit is contained in:
183
cms/protocol/pssoaep.go
Normal file
183
cms/protocol/pssoaep.go
Normal file
@ -0,0 +1,183 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
|
||||
oid "github.com/InfiniteLoopSpace/go_S-MIME/oid"
|
||||
)
|
||||
|
||||
type pssParameters struct {
|
||||
Hash pkix.AlgorithmIdentifier `asn1:"optional,explicit,tag:0"`
|
||||
MGF pkix.AlgorithmIdentifier `asn1:"optional,explicit,tag:1"`
|
||||
SaltLength int `asn1:"optional,explicit,tag:2"`
|
||||
TrailerField int `asn1:"optional,explicit,tag:3"` //,default:1"`
|
||||
}
|
||||
|
||||
var oidMGF1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 8}
|
||||
|
||||
func newpssParameters(hash ...crypto.Hash) (param pssParameters, err error) {
|
||||
|
||||
// SHA1 is default value
|
||||
if len(hash) == 1 && hash[0] == crypto.SHA1 {
|
||||
return
|
||||
}
|
||||
|
||||
var h asn1.ObjectIdentifier
|
||||
|
||||
if len(hash) == 0 {
|
||||
h = oid.DigestAlgorithmSHA1
|
||||
param.SaltLength = 20
|
||||
param.TrailerField = 1
|
||||
} else {
|
||||
var ok bool
|
||||
h, ok = oid.HashToDigestAlgorithm[hash[0]]
|
||||
if !ok {
|
||||
err = errors.New("Unsupported hashfunction")
|
||||
}
|
||||
}
|
||||
|
||||
hRV, err := RawValue(pkix.AlgorithmIdentifier{Algorithm: h})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
param.Hash = pkix.AlgorithmIdentifier{Algorithm: h}
|
||||
|
||||
param.MGF = pkix.AlgorithmIdentifier{Algorithm: oidMGF1, Parameters: hRV}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// This is needed because CheckSignature uses PSSSaltLengthEqualsHash, if PSSSaltLengthAuto is used this code is not needed
|
||||
func verfiyRSAPSS(cert x509.Certificate, signatureAlgorithm pkix.AlgorithmIdentifier, signedMessage, signature []byte) (err error) {
|
||||
|
||||
param, err := newpssParameters()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = asn1.Unmarshal(signatureAlgorithm.Parameters.FullBytes, ¶m)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !param.MGF.Algorithm.Equal(oidMGF1) {
|
||||
err = errors.New("Mask generator funktion not supported; only MGF1 is supported")
|
||||
return
|
||||
}
|
||||
|
||||
hash := oid.DigestAlgorithmToHash[param.Hash.Algorithm.String()]
|
||||
|
||||
pssOpts := rsa.PSSOptions{SaltLength: param.SaltLength, Hash: hash}
|
||||
|
||||
h := hash.New()
|
||||
h.Write(signedMessage)
|
||||
digest := h.Sum(nil)
|
||||
|
||||
err = rsa.VerifyPSS(cert.PublicKey.(*rsa.PublicKey), hash, digest, signature, &pssOpts)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func newPSS(hash crypto.Hash, pub *rsa.PublicKey) (signatureAlgorithm pkix.AlgorithmIdentifier, opts *rsa.PSSOptions, err error) {
|
||||
|
||||
opts = &rsa.PSSOptions{Hash: hash}
|
||||
|
||||
pssParam, err := newpssParameters(hash)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
pssParam.SaltLength = (pub.N.BitLen()+7)/8 - 2 - hash.Size() // https://golang.org/src/crypto/rsa/pss.go?s=6982:7095#L239
|
||||
|
||||
paramRV, err := RawValue(pssParam)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
signatureAlgorithm = pkix.AlgorithmIdentifier{Algorithm: oid.SignatureAlgorithmRSASSAPSS, Parameters: paramRV}
|
||||
return
|
||||
}
|
||||
|
||||
// RSAESOAEPparams ::= SEQUENCE {
|
||||
// hashFunc [0] AlgorithmIdentifier DEFAULT sha1Identifier,
|
||||
// maskGenFunc [1] AlgorithmIdentifier DEFAULT mgf1SHA1Identifier,
|
||||
// pSourceFunc [2] AlgorithmIdentifier DEFAULT
|
||||
// pSpecifiedEmptyIdentifier }
|
||||
type RSAESOAEPparams struct {
|
||||
HashFunc pkix.AlgorithmIdentifier `asn1:"optional,explicit,tag:0"`
|
||||
MaskGenFunc pkix.AlgorithmIdentifier `asn1:"optional,explicit,tag:1"`
|
||||
PSourceFunc pkix.AlgorithmIdentifier `asn1:"optional,explicit,tag:2"`
|
||||
}
|
||||
|
||||
var oidpSpecified = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 9}
|
||||
|
||||
func newRSAESOAEPparams(hash ...crypto.Hash) (param RSAESOAEPparams, err error) {
|
||||
|
||||
// SHA1 is default value
|
||||
if len(hash) == 1 && hash[0] == crypto.SHA1 {
|
||||
return
|
||||
}
|
||||
|
||||
nullOctetString, err := RawValue([]byte{})
|
||||
|
||||
var h asn1.ObjectIdentifier
|
||||
|
||||
if len(hash) == 0 {
|
||||
h = oid.DigestAlgorithmSHA1
|
||||
} else {
|
||||
var ok bool
|
||||
h, ok = oid.HashToDigestAlgorithm[hash[0]]
|
||||
if !ok {
|
||||
err = errors.New("Unsupported hashfunction")
|
||||
}
|
||||
}
|
||||
|
||||
hRV, err := RawValue(pkix.AlgorithmIdentifier{Algorithm: h})
|
||||
|
||||
param = RSAESOAEPparams{pkix.AlgorithmIdentifier{Algorithm: h, Parameters: asn1.NullRawValue},
|
||||
pkix.AlgorithmIdentifier{Algorithm: oidMGF1, Parameters: hRV},
|
||||
pkix.AlgorithmIdentifier{Algorithm: oidpSpecified, Parameters: nullOctetString}}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func parseRSAESOAEPparams(param []byte) (opts *rsa.OAEPOptions, err error) {
|
||||
var oaepOpts RSAESOAEPparams
|
||||
oaepOpts, err = newRSAESOAEPparams()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = asn1.Unmarshal(param, &oaepOpts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
opts = &rsa.OAEPOptions{Hash: oid.DigestAlgorithmToHash[oaepOpts.HashFunc.Algorithm.String()], Label: []byte{}}
|
||||
|
||||
if !oaepOpts.MaskGenFunc.Algorithm.Equal(oidMGF1) {
|
||||
err = errors.New("Unsupported mask generation funktion" + oaepOpts.MaskGenFunc.Algorithm.String())
|
||||
return
|
||||
}
|
||||
|
||||
if !oaepOpts.PSourceFunc.Algorithm.Equal(oidpSpecified) {
|
||||
err = errors.New("Unsupported p source funktion" + oaepOpts.PSourceFunc.Algorithm.String())
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func isRSAPSS(cert *x509.Certificate) bool {
|
||||
switch cert.SignatureAlgorithm {
|
||||
case x509.SHA256WithRSAPSS, x509.SHA384WithRSAPSS, x509.SHA512WithRSAPSS:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
@ -10,7 +10,6 @@ import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
@ -34,7 +33,7 @@ type RecipientInfo struct {
|
||||
func (recInfo *RecipientInfo) decryptKey(keyPair tls.Certificate) (key []byte, err error) {
|
||||
|
||||
key, err = recInfo.KTRI.decryptKey(keyPair)
|
||||
if key != nil {
|
||||
if key != nil || (err != nil && err != ErrUnsupported) {
|
||||
return
|
||||
}
|
||||
|
||||
@ -64,6 +63,23 @@ func (ktri *KeyTransRecipientInfo) decryptKey(keyPair tls.Certificate) (key []by
|
||||
|
||||
ski := keyPair.Leaf.SubjectKeyId
|
||||
|
||||
certPubAlg := oid.PublicKeyAlgorithmToEncrytionAlgorithm[keyPair.Leaf.PublicKeyAlgorithm].Algorithm
|
||||
|
||||
var decOpts crypto.DecrypterOpts
|
||||
pkcs15CertwithOAEP := false
|
||||
|
||||
if ktri.KeyEncryptionAlgorithm.Algorithm.Equal(oid.EncryptionAlgorithmRSAESOAEP) {
|
||||
|
||||
if certPubAlg.Equal(oid.EncryptionAlgorithmRSA) {
|
||||
pkcs15CertwithOAEP = true
|
||||
}
|
||||
|
||||
decOpts, err = parseRSAESOAEPparams(ktri.KeyEncryptionAlgorithm.Parameters.FullBytes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
//version is the syntax version number. If the SignerIdentifier is
|
||||
//the CHOICE issuerAndSerialNumber, then the version MUST be 1. If
|
||||
//the SignerIdentifier is subjectKeyIdentifier, then the version
|
||||
@ -71,28 +87,25 @@ func (ktri *KeyTransRecipientInfo) decryptKey(keyPair tls.Certificate) (key []by
|
||||
switch ktri.Version {
|
||||
case 0:
|
||||
if ias.Equal(ktri.Rid.IAS) {
|
||||
alg := oid.PublicKeyAlgorithmToEncrytionAlgorithm[keyPair.Leaf.PublicKeyAlgorithm].Algorithm
|
||||
if ktri.KeyEncryptionAlgorithm.Algorithm.Equal(alg) {
|
||||
if ktri.KeyEncryptionAlgorithm.Algorithm.Equal(certPubAlg) || pkcs15CertwithOAEP {
|
||||
|
||||
decrypter := keyPair.PrivateKey.(crypto.Decrypter)
|
||||
return decrypter.Decrypt(rand.Reader, ktri.EncryptedKey, nil)
|
||||
return decrypter.Decrypt(rand.Reader, ktri.EncryptedKey, decOpts)
|
||||
|
||||
}
|
||||
log.Println("Key encrytion algorithm not matching")
|
||||
}
|
||||
case 2:
|
||||
if bytes.Equal(ski, ktri.Rid.SKI) {
|
||||
alg := oid.PublicKeyAlgorithmToEncrytionAlgorithm[keyPair.Leaf.PublicKeyAlgorithm].Algorithm
|
||||
if ktri.KeyEncryptionAlgorithm.Algorithm.Equal(alg) {
|
||||
if alg.Equal(oid.EncryptionAlgorithmRSA) {
|
||||
return rsa.DecryptPKCS1v15(rand.Reader, keyPair.PrivateKey.(*rsa.PrivateKey), ktri.EncryptedKey)
|
||||
}
|
||||
log.Println("Unsupported key encrytion algorithm")
|
||||
if ktri.KeyEncryptionAlgorithm.Algorithm.Equal(certPubAlg) || pkcs15CertwithOAEP {
|
||||
|
||||
decrypter := keyPair.PrivateKey.(crypto.Decrypter)
|
||||
return decrypter.Decrypt(rand.Reader, ktri.EncryptedKey, decOpts)
|
||||
|
||||
}
|
||||
log.Println("Key encrytion algorithm not matching")
|
||||
}
|
||||
default:
|
||||
fmt.Println(ktri.Version)
|
||||
return nil, ErrUnsupported
|
||||
}
|
||||
|
||||
@ -109,35 +122,15 @@ type RecipientIdentifier struct {
|
||||
|
||||
// NewRecipientInfo creates RecipientInfo for giben recipient and key.
|
||||
func NewRecipientInfo(recipient *x509.Certificate, key []byte) (info RecipientInfo, err error) {
|
||||
version := 0 //issuerAndSerialNumber
|
||||
|
||||
rid := RecipientIdentifier{}
|
||||
|
||||
switch version {
|
||||
case 0:
|
||||
ias, err := NewIssuerAndSerialNumber(recipient)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
rid.IAS = ias
|
||||
case 2:
|
||||
rid.SKI = recipient.SubjectKeyId
|
||||
}
|
||||
|
||||
switch recipient.PublicKeyAlgorithm {
|
||||
case x509.RSA:
|
||||
var encrypted []byte
|
||||
encrypted, err = encryptKeyRSA(key, recipient)
|
||||
var ktri KeyTransRecipientInfo
|
||||
ktri, err = encryptKeyRSA(key, recipient)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
info = RecipientInfo{
|
||||
KTRI: KeyTransRecipientInfo{
|
||||
Version: version,
|
||||
Rid: rid,
|
||||
KeyEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oid.EncryptionAlgorithmRSA},
|
||||
EncryptedKey: encrypted,
|
||||
}}
|
||||
info = RecipientInfo{KTRI: ktri}
|
||||
case x509.ECDSA:
|
||||
var kari KeyAgreeRecipientInfo
|
||||
kari, err = encryptKeyECDH(key, recipient)
|
||||
@ -152,11 +145,47 @@ func NewRecipientInfo(recipient *x509.Certificate, key []byte) (info RecipientIn
|
||||
return
|
||||
}
|
||||
|
||||
func encryptKeyRSA(key []byte, recipient *x509.Certificate) ([]byte, error) {
|
||||
if pub := recipient.PublicKey.(*rsa.PublicKey); pub != nil {
|
||||
return rsa.EncryptPKCS1v15(rand.Reader, pub, key)
|
||||
func encryptKeyRSA(key []byte, recipient *x509.Certificate) (ktri KeyTransRecipientInfo, err error) {
|
||||
ktri.Version = 0 //issuerAndSerialNumber
|
||||
|
||||
switch ktri.Version {
|
||||
case 0:
|
||||
ias, err := NewIssuerAndSerialNumber(recipient)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
ktri.Rid.IAS = ias
|
||||
case 2:
|
||||
ktri.Rid.SKI = recipient.SubjectKeyId
|
||||
}
|
||||
return nil, ErrUnsupportedAlgorithm
|
||||
|
||||
if pub := recipient.PublicKey.(*rsa.PublicKey); pub != nil {
|
||||
|
||||
if isRSAPSS(recipient) {
|
||||
hash := crypto.SHA256
|
||||
var oaepparam RSAESOAEPparams
|
||||
oaepparam, err = newRSAESOAEPparams(hash)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var oaepparamRV asn1.RawValue
|
||||
oaepparamRV, err = RawValue(oaepparam)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ktri.KeyEncryptionAlgorithm = pkix.AlgorithmIdentifier{Algorithm: oid.EncryptionAlgorithmRSAESOAEP, Parameters: oaepparamRV}
|
||||
h := hash.New()
|
||||
ktri.EncryptedKey, err = rsa.EncryptOAEP(h, rand.Reader, pub, key, nil)
|
||||
return
|
||||
}
|
||||
|
||||
ktri.KeyEncryptionAlgorithm = pkix.AlgorithmIdentifier{Algorithm: oid.EncryptionAlgorithmRSA}
|
||||
ktri.EncryptedKey, err = rsa.EncryptPKCS1v15(rand.Reader, pub, key)
|
||||
return
|
||||
}
|
||||
|
||||
err = ErrUnsupportedAlgorithm
|
||||
return
|
||||
}
|
||||
|
||||
// ErrUnsupportedAlgorithm is returned if the algorithm is unsupported.
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
@ -106,7 +107,7 @@ func NewSignedData(eci EncapsulatedContentInfo) (*SignedData, error) {
|
||||
}
|
||||
|
||||
// AddSignerInfo adds a SignerInfo to the SignedData.
|
||||
func (sd *SignedData) AddSignerInfo(keypPair tls.Certificate) (err error) {
|
||||
func (sd *SignedData) AddSignerInfo(keypPair tls.Certificate, attrs []Attribute) (err error) {
|
||||
|
||||
for _, cert := range keypPair.Certificate {
|
||||
if err = sd.AddCertificate(cert); err != nil {
|
||||
@ -125,8 +126,13 @@ func (sd *SignedData) AddSignerInfo(keypPair tls.Certificate) (err error) {
|
||||
|
||||
sid := SignerIdentifier{ias, nil}
|
||||
|
||||
var signerOpts crypto.SignerOpts
|
||||
digestAlgorithm := digestAlgorithmForPublicKey(cert.PublicKey)
|
||||
signatureAlgorithm, ok := oid.PublicKeyAlgorithmToSignatureAlgorithm[keypPair.Leaf.PublicKeyAlgorithm]
|
||||
if isRSAPSS(cert) {
|
||||
h := oid.DigestAlgorithmToHash[digestAlgorithm.Algorithm.String()]
|
||||
signatureAlgorithm, signerOpts, err = newPSS(h, cert.PublicKey.(*rsa.PublicKey))
|
||||
}
|
||||
if !ok {
|
||||
return errors.New("unsupported certificate public key algorithm")
|
||||
}
|
||||
@ -155,6 +161,11 @@ func (sd *SignedData) AddSignerInfo(keypPair tls.Certificate) (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !isRSAPSS(cert) {
|
||||
signerOpts = hash
|
||||
}
|
||||
|
||||
md := hash.New()
|
||||
if _, err = md.Write(content); err != nil {
|
||||
return err
|
||||
@ -174,6 +185,7 @@ func (sd *SignedData) AddSignerInfo(keypPair tls.Certificate) (err error) {
|
||||
return err
|
||||
}
|
||||
si.SignedAttrs = append(si.SignedAttrs, mdAttr, ctAttr, sTAttr)
|
||||
si.SignedAttrs = append(si.SignedAttrs, attrs...)
|
||||
|
||||
sm, err := asn.MarshalWithParams(si.SignedAttrs, `set`)
|
||||
if err != nil {
|
||||
@ -184,7 +196,7 @@ func (sd *SignedData) AddSignerInfo(keypPair tls.Certificate) (err error) {
|
||||
if _, errr := smd.Write(sm); errr != nil {
|
||||
return errr
|
||||
}
|
||||
if si.Signature, err = signer.Sign(rand.Reader, smd.Sum(nil), hash); err != nil {
|
||||
if si.Signature, err = signer.Sign(rand.Reader, smd.Sum(nil), signerOpts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -384,7 +396,11 @@ func (sd *SignedData) Verify(Opts x509.VerifyOptions, detached []byte) (chains [
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = cert.CheckSignature(sigAlg, signedMessage, signer.Signature)
|
||||
switch signer.SignatureAlgorithm.Algorithm.String() {
|
||||
case oid.SignatureAlgorithmRSASSAPSS.String():
|
||||
default:
|
||||
err = cert.CheckSignature(sigAlg, signedMessage, signer.Signature)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
Reference in New Issue
Block a user