Added support for RSASSA-PSS and RSAES-OAEP.

This commit is contained in:
InfiniteLoopSpace 2018-12-21 14:43:59 +01:00
parent 5f34d82562
commit 3f58f9a4b2
10 changed files with 510 additions and 181 deletions

View File

@ -14,12 +14,14 @@ It consists of the following packages
- smime Secure/Multipurpose Internet Mail Extensions (S/MIME) Version 4.0 [rfc5751-bis-12](https://tools.ietf.org/html/draft-ietf-lamps-rfc5751-bis-12) [![GoDoc](https://godoc.org/github.com/InfiniteLoopSpace/go_S-MIME/smime?status.svg)](https://godoc.org/github.com/InfiniteLoopSpace/go_S-MIME/smime) - smime Secure/Multipurpose Internet Mail Extensions (S/MIME) Version 4.0 [rfc5751-bis-12](https://tools.ietf.org/html/draft-ietf-lamps-rfc5751-bis-12) [![GoDoc](https://godoc.org/github.com/InfiniteLoopSpace/go_S-MIME/smime?status.svg)](https://godoc.org/github.com/InfiniteLoopSpace/go_S-MIME/smime)
- timestamp<sup>[5]</sup> - Time-Stamp Protocol (TSP) [rfc3161](https://tools.ietf.org/html/rfc3161) [![GoDoc](https://godoc.org/github.com/InfiniteLoopSpace/go_S-MIME/timestamp?status.svg)](https://godoc.org/github.com/InfiniteLoopSpace/go_S-MIME/timestamp) - timestamp<sup>[5]</sup> - Time-Stamp Protocol (TSP) [rfc3161](https://tools.ietf.org/html/rfc3161) [![GoDoc](https://godoc.org/github.com/InfiniteLoopSpace/go_S-MIME/timestamp?status.svg)](https://godoc.org/github.com/InfiniteLoopSpace/go_S-MIME/timestamp)
It supports enveloped data with AES in CBC mode. Decryption also works with (3)DES. Authenticated-Enveloped-Data Content Type is also supported with AES-GCM and ChaCha20-Poly1305. It supports enveloped data with AES in CBC mode. Decryption also works with (3)DES. Authenticated-Enveloped-Data Content Type is also supported with AES-GCM and ChaCha20-Poly1305. Also RSAES-OAEP and RSASSA-PSS is supported.
This is covered in This is covered in
- Cryptographic Message Syntax (CMS) Authenticated-Enveloped-Data Content Type [rfc5083](https://tools.ietf.org/html/rfc5083) - Cryptographic Message Syntax (CMS) Authenticated-Enveloped-Data Content Type [rfc5083](https://tools.ietf.org/html/rfc5083)
- Using ChaCha20-Poly1305 Authenticated Encryption in the Cryptographic Message Syntax (CMS) [rfc8103](https://tools.ietf.org/html/rfc8103) - Using ChaCha20-Poly1305 Authenticated Encryption in the Cryptographic Message Syntax (CMS) [rfc8103](https://tools.ietf.org/html/rfc8103)
- Using AES-CCM and AES-GCM Authenticated Encryption in the Cryptographic Message Syntax (CMS) [rfc5084](https://tools.ietf.org/html/rfc5084) - Using AES-CCM and AES-GCM Authenticated Encryption in the Cryptographic Message Syntax (CMS) [rfc5084](https://tools.ietf.org/html/rfc5084)
- Use of the RSASSA-PSS Signature Algorithm in Cryptographic Message Syntax (CMS) [rfc4056](https://tools.ietf.org/html/rfc4056)
- Use of the RSAES-OAEP Key Transport Algorithm in the Cryptographic Message Syntax (CMS) [rfc3560](https://tools.ietf.org/html/rfc3560)
## Examples ## Examples
@ -53,8 +55,6 @@ plaintext, _ := SMIME.Verify(signedMsg)
## Todo ## Todo
- Add S/MIME capabilities attributes
- Add ECDH for encryption and decryption
- Testing - Testing

View File

@ -23,6 +23,7 @@ type CMS struct {
TimeStampServer string TimeStampServer string
TimeStamp bool TimeStamp bool
keyPairs []tls.Certificate keyPairs []tls.Certificate
signedAttrs []protocol.Attribute
} }
// New create a new instance of CMS with given keyPairs. // New create a new instance of CMS with given keyPairs.
@ -54,6 +55,19 @@ func New(cert ...tls.Certificate) (cms *CMS, err error) {
return return
} }
// AddAttribute adds a attribute to signedAttrs which will be used for signing
func (cms *CMS) AddAttribute(attrType asn1.ObjectIdentifier, val interface{}) (err error) {
attr, err := protocol.NewAttribute(attrType, val)
if err != nil {
return
}
cms.signedAttrs = append(cms.signedAttrs, attr)
return
}
// Encrypt encrypts data for the recipients and returns DER-encoded ASN.1 ContentInfo. // Encrypt encrypts data for the recipients and returns DER-encoded ASN.1 ContentInfo.
func (cms *CMS) Encrypt(data []byte, recipients []*x509.Certificate) (der []byte, err error) { func (cms *CMS) Encrypt(data []byte, recipients []*x509.Certificate) (der []byte, err error) {
@ -160,7 +174,10 @@ func (cms *CMS) Sign(data []byte, detachedSignature ...bool) (der []byte, err er
} }
for i := range cms.keyPairs { for i := range cms.keyPairs {
sd.AddSignerInfo(cms.keyPairs[i]) err = sd.AddSignerInfo(cms.keyPairs[i], cms.signedAttrs)
if err != nil {
return
}
} }
if cms.TimeStamp { if cms.TimeStamp {

View File

@ -29,216 +29,250 @@ var (
CommonName: "leaf.example.com", CommonName: "leaf.example.com",
})) }))
leafPSS = intermediate.Issue(pki.Subject(pkix.Name{
CommonName: "leaf.example.com",
}), pki.SignatureAlgorithm(x509.SHA256WithRSAPSS))
ecKey, _ = ecdsa.GenerateKey(elliptic.P384(), rand.Reader) ecKey, _ = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
leafECC = intermediate.Issue(pki.Subject(pkix.Name{ leafECC = intermediate.Issue(pki.Subject(pkix.Name{
CommonName: "leaf.example.com", CommonName: "leaf.example.com",
}), pki.PrivateKey(ecKey)) }), pki.PrivateKey(ecKey))
keyPair = tls.Certificate{ keyPairRSA = tls.Certificate{
Certificate: [][]byte{leaf.Certificate.Raw, intermediate.Certificate.Raw, root.Certificate.Raw}, Certificate: [][]byte{leaf.Certificate.Raw, intermediate.Certificate.Raw, root.Certificate.Raw},
PrivateKey: leaf.PrivateKey.(crypto.PrivateKey), PrivateKey: leaf.PrivateKey.(crypto.PrivateKey),
Leaf: leaf.Certificate, Leaf: leaf.Certificate,
} }
keyPairRSAPSS = tls.Certificate{
Certificate: [][]byte{leafPSS.Certificate.Raw, intermediate.Certificate.Raw, root.Certificate.Raw},
PrivateKey: leafPSS.PrivateKey.(crypto.PrivateKey),
Leaf: leafPSS.Certificate,
}
keyPairECC = tls.Certificate{ keyPairECC = tls.Certificate{
Certificate: [][]byte{leafECC.Certificate.Raw, intermediate.Certificate.Raw, root.Certificate.Raw}, Certificate: [][]byte{leafECC.Certificate.Raw, intermediate.Certificate.Raw, root.Certificate.Raw},
PrivateKey: leafECC.PrivateKey.(crypto.PrivateKey), PrivateKey: leafECC.PrivateKey.(crypto.PrivateKey),
Leaf: leafECC.Certificate, Leaf: leafECC.Certificate,
} }
keyPairs = []tls.Certificate{} keypair tls.Certificate
keyPairsOpenssl = []tls.Certificate{} skipOpenssl = false
opensslSignOpts = []string{"-outform", "DER"}
opensslEncOpts = []string{"-outform", "DER"}
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
// Test RSA
keyPairs = []tls.Certificate{keyPair, keyPairECC} keypair = keyPairRSA
version, err := openssl.Openssl(nil, "version") version, err := openssl.Openssl(nil, "version")
if err != nil {
if err == nil { skipOpenssl = true
keyPairsOpenssl = append(keyPairsOpenssl, keyPair)
} }
m.Run()
// Test RSA PSS OAEP
keypair = keyPairRSAPSS
if strings.HasPrefix(string(version), "OpenSSL 1.1") { if strings.HasPrefix(string(version), "OpenSSL 1.1") {
openssl.SMIME = "cms" openssl.SMIME = "cms"
keyPairsOpenssl = append(keyPairsOpenssl, keyPairECC) } else {
skipOpenssl = true
} }
opensslSignOpts = append(opensslSignOpts, "-keyopt", "rsa_padding_mode:pss")
opensslEncOpts = append(opensslEncOpts, "-keyopt", "rsa_padding_mode:oaep")
m.Run()
opensslSignOpts = []string{"-outform", "DER"}
opensslEncOpts = []string{"-outform", "DER"}
// Test ECDDSA
keypair = keyPairECC
m.Run() m.Run()
} }
func TestAuthEnrypt(t *testing.T) { func TestAuthEnrypt(t *testing.T) {
for _, keypair := range keyPairs { cms, err := New(keypair)
cms, err := New(keypair) if err != nil {
if err != nil { t.Error(err)
t.Error(err) }
}
plaintext := []byte("Hallo Welt!") plaintext := []byte("Hallo Welt!")
ciphertext, err := cms.AuthEncrypt(plaintext, []*x509.Certificate{keypair.Leaf}) ciphertext, err := cms.AuthEncrypt(plaintext, []*x509.Certificate{keypair.Leaf})
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
plain, err := cms.AuthDecrypt(ciphertext) plain, err := cms.AuthDecrypt(ciphertext)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
if !bytes.Equal(plaintext, plain) { if !bytes.Equal(plaintext, plain) {
t.Fatal("Encryption and decryption are not inverse") t.Fatal("Encryption and decryption are not inverse")
}
} }
} }
func TestEnryptDecrypt(t *testing.T) { func TestEnryptDecrypt(t *testing.T) {
for _, keypair := range keyPairs { cms, err := New(keypair)
cms, err := New(keypair) if err != nil {
if err != nil { t.Error(err)
t.Error(err)
}
plaintext := []byte("Hallo Welt!")
ciphertext, err := cms.Encrypt(plaintext, []*x509.Certificate{keypair.Leaf})
if err != nil {
t.Error(err)
}
plain, err := cms.Decrypt(ciphertext)
if err != nil {
t.Error(err)
}
if !bytes.Equal(plaintext, plain) {
t.Fatal("Encryption and decryption are not inverse")
}
} }
plaintext := []byte("Hallo Welt!")
ciphertext, err := cms.Encrypt(plaintext, []*x509.Certificate{keypair.Leaf})
if err != nil {
t.Error(err)
}
plain, err := cms.Decrypt(ciphertext)
if err != nil {
t.Error(err)
}
if !bytes.Equal(plaintext, plain) {
t.Fatal("Encryption and decryption are not inverse")
}
} }
func TestSignVerify(t *testing.T) { func TestSignVerify(t *testing.T) {
for _, keypair := range keyPairs { cms, err := New(keypair)
cms, err := New(keypair) if err != nil {
if err != nil { t.Error(err)
t.Error(err) }
}
cms.roots.AddCert(root.Certificate) cms.roots.AddCert(root.Certificate)
msg := []byte("Hallo Welt!") msg := []byte("Hallo Welt!")
der, err := cms.Sign(msg) der, err := cms.Sign(msg)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
_, err = cms.Verify(der) _, err = cms.Verify(der)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
}
} }
} }
func TestEncryptOpenSSL(t *testing.T) { func TestEncryptOpenSSL(t *testing.T) {
for _, keypair := range keyPairsOpenssl { if skipOpenssl {
message := []byte("Hallo Welt!") return
der, err := openssl.Encrypt(message, keypair.Leaf, "-outform", "DER")
if err != nil {
t.Error(err)
}
cms, err := New(keypair)
plain, err := cms.Decrypt(der)
if err != nil {
t.Error(err)
}
if !bytes.Equal(message, plain) {
t.Fatal("Encryption and decryption are not inverse")
}
} }
message := []byte("Hallo Welt!")
der, err := openssl.Encrypt(message, keypair.Leaf, opensslEncOpts...)
if err != nil {
t.Error(err)
}
cms, err := New(keypair)
plain, err := cms.Decrypt(der)
if err != nil {
t.Error(err)
}
if !bytes.Equal(message, plain) {
t.Fatal("Encryption and decryption are not inverse")
}
} }
func TestDecryptOpenSSL(t *testing.T) { func TestDecryptOpenSSL(t *testing.T) {
for _, keypair := range keyPairsOpenssl { if skipOpenssl {
message := []byte("Hallo Welt!") return
cms, _ := New()
ciphertext, err := cms.Encrypt(message, []*x509.Certificate{keypair.Leaf})
if err != nil {
t.Error(err)
}
plain, err := openssl.Decrypt(ciphertext, keypair.PrivateKey, "-inform", "DER")
if err != nil {
t.Error(err)
}
if !bytes.Equal(message, plain) {
t.Fatal("Encryption and decryption are not inverse")
}
} }
message := []byte("Hallo Welt!")
cms, _ := New()
ciphertext, err := cms.Encrypt(message, []*x509.Certificate{keypair.Leaf})
if err != nil {
t.Error(err)
}
plain, err := openssl.Decrypt(ciphertext, keypair.PrivateKey, "-inform", "DER")
if err != nil {
t.Error(err)
}
if !bytes.Equal(message, plain) {
t.Fatal("Encryption and decryption are not inverse")
}
} }
func TestSignOpenSSL(t *testing.T) { func TestSignOpenSSL(t *testing.T) {
for _, keypair := range keyPairsOpenssl { if skipOpenssl {
message := []byte("Hallo Welt") return
}
sig, err := openssl.SignDetached(message, keypair.Leaf, keypair.PrivateKey, []*x509.Certificate{intermediate.Certificate}, "-outform", "DER") message := []byte("Hallo Welt")
if err != nil {
t.Error(err)
}
cms, err := New() sig, err := openssl.SignDetached(message, keypair.Leaf, keypair.PrivateKey, []*x509.Certificate{intermediate.Certificate}, opensslSignOpts...)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
cms.roots.AddCert(root.Certificate)
_, err = cms.Verify(sig) cms, err := New()
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
cms.roots.AddCert(root.Certificate)
_, err = cms.Verify(sig)
if err != nil {
t.Error(err)
} }
} }
func TestVerifyOpenSSL(t *testing.T) { func TestVerifyOpenSSL(t *testing.T) {
for _, keypair := range keyPairsOpenssl { if skipOpenssl {
cms, err := New(keypair) return
if err != nil { }
t.Error(err)
}
cms.TimeStamp = true cms, err := New(keypair)
if err != nil {
t.Error(err)
}
cms.roots.AddCert(root.Certificate) cms.TimeStamp = true
msg := []byte("Hallo Welt!") cms.roots.AddCert(root.Certificate)
der, err := cms.Sign(msg) msg := []byte("Hallo Welt!")
if err != nil {
t.Error(err)
}
sig, err := openssl.Verify(der, root.Certificate, "-inform", "DER") der, err := cms.Sign(msg)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
if !bytes.Equal(msg, sig) { sig, err := openssl.Verify(der, root.Certificate, "-inform", "DER")
t.Fatal("Signed message and message do not agree!") if err != nil {
} t.Error(err)
}
if !bytes.Equal(msg, sig) {
t.Fatal("Signed message and message do not agree!")
} }
} }

183
cms/protocol/pssoaep.go Normal file
View 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, &param)
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
}
}

View File

@ -10,7 +10,6 @@ import (
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/asn1" "encoding/asn1"
"errors" "errors"
"fmt"
"log" "log"
"time" "time"
@ -34,7 +33,7 @@ type RecipientInfo struct {
func (recInfo *RecipientInfo) decryptKey(keyPair tls.Certificate) (key []byte, err error) { func (recInfo *RecipientInfo) decryptKey(keyPair tls.Certificate) (key []byte, err error) {
key, err = recInfo.KTRI.decryptKey(keyPair) key, err = recInfo.KTRI.decryptKey(keyPair)
if key != nil { if key != nil || (err != nil && err != ErrUnsupported) {
return return
} }
@ -64,6 +63,23 @@ func (ktri *KeyTransRecipientInfo) decryptKey(keyPair tls.Certificate) (key []by
ski := keyPair.Leaf.SubjectKeyId 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 //version is the syntax version number. If the SignerIdentifier is
//the CHOICE issuerAndSerialNumber, then the version MUST be 1. If //the CHOICE issuerAndSerialNumber, then the version MUST be 1. If
//the SignerIdentifier is subjectKeyIdentifier, then the version //the SignerIdentifier is subjectKeyIdentifier, then the version
@ -71,28 +87,25 @@ func (ktri *KeyTransRecipientInfo) decryptKey(keyPair tls.Certificate) (key []by
switch ktri.Version { switch ktri.Version {
case 0: case 0:
if ias.Equal(ktri.Rid.IAS) { if ias.Equal(ktri.Rid.IAS) {
alg := oid.PublicKeyAlgorithmToEncrytionAlgorithm[keyPair.Leaf.PublicKeyAlgorithm].Algorithm if ktri.KeyEncryptionAlgorithm.Algorithm.Equal(certPubAlg) || pkcs15CertwithOAEP {
if ktri.KeyEncryptionAlgorithm.Algorithm.Equal(alg) {
decrypter := keyPair.PrivateKey.(crypto.Decrypter) 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") log.Println("Key encrytion algorithm not matching")
} }
case 2: case 2:
if bytes.Equal(ski, ktri.Rid.SKI) { if bytes.Equal(ski, ktri.Rid.SKI) {
alg := oid.PublicKeyAlgorithmToEncrytionAlgorithm[keyPair.Leaf.PublicKeyAlgorithm].Algorithm if ktri.KeyEncryptionAlgorithm.Algorithm.Equal(certPubAlg) || pkcs15CertwithOAEP {
if ktri.KeyEncryptionAlgorithm.Algorithm.Equal(alg) {
if alg.Equal(oid.EncryptionAlgorithmRSA) { decrypter := keyPair.PrivateKey.(crypto.Decrypter)
return rsa.DecryptPKCS1v15(rand.Reader, keyPair.PrivateKey.(*rsa.PrivateKey), ktri.EncryptedKey) return decrypter.Decrypt(rand.Reader, ktri.EncryptedKey, decOpts)
}
log.Println("Unsupported key encrytion algorithm")
} }
log.Println("Key encrytion algorithm not matching") log.Println("Key encrytion algorithm not matching")
} }
default: default:
fmt.Println(ktri.Version)
return nil, ErrUnsupported return nil, ErrUnsupported
} }
@ -109,35 +122,15 @@ type RecipientIdentifier struct {
// NewRecipientInfo creates RecipientInfo for giben recipient and key. // NewRecipientInfo creates RecipientInfo for giben recipient and key.
func NewRecipientInfo(recipient *x509.Certificate, key []byte) (info RecipientInfo, err error) { 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 { switch recipient.PublicKeyAlgorithm {
case x509.RSA: case x509.RSA:
var encrypted []byte var ktri KeyTransRecipientInfo
encrypted, err = encryptKeyRSA(key, recipient) ktri, err = encryptKeyRSA(key, recipient)
if err != nil { if err != nil {
return return
} }
info = RecipientInfo{ info = RecipientInfo{KTRI: ktri}
KTRI: KeyTransRecipientInfo{
Version: version,
Rid: rid,
KeyEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oid.EncryptionAlgorithmRSA},
EncryptedKey: encrypted,
}}
case x509.ECDSA: case x509.ECDSA:
var kari KeyAgreeRecipientInfo var kari KeyAgreeRecipientInfo
kari, err = encryptKeyECDH(key, recipient) kari, err = encryptKeyECDH(key, recipient)
@ -152,11 +145,47 @@ func NewRecipientInfo(recipient *x509.Certificate, key []byte) (info RecipientIn
return return
} }
func encryptKeyRSA(key []byte, recipient *x509.Certificate) ([]byte, error) { func encryptKeyRSA(key []byte, recipient *x509.Certificate) (ktri KeyTransRecipientInfo, err error) {
if pub := recipient.PublicKey.(*rsa.PublicKey); pub != nil { ktri.Version = 0 //issuerAndSerialNumber
return rsa.EncryptPKCS1v15(rand.Reader, pub, key)
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. // ErrUnsupportedAlgorithm is returned if the algorithm is unsupported.

View File

@ -6,6 +6,7 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
"crypto/rsa"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"crypto/x509/pkix" "crypto/x509/pkix"
@ -106,7 +107,7 @@ func NewSignedData(eci EncapsulatedContentInfo) (*SignedData, error) {
} }
// AddSignerInfo adds a SignerInfo to the SignedData. // 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 { for _, cert := range keypPair.Certificate {
if err = sd.AddCertificate(cert); err != nil { if err = sd.AddCertificate(cert); err != nil {
@ -125,8 +126,13 @@ func (sd *SignedData) AddSignerInfo(keypPair tls.Certificate) (err error) {
sid := SignerIdentifier{ias, nil} sid := SignerIdentifier{ias, nil}
var signerOpts crypto.SignerOpts
digestAlgorithm := digestAlgorithmForPublicKey(cert.PublicKey) digestAlgorithm := digestAlgorithmForPublicKey(cert.PublicKey)
signatureAlgorithm, ok := oid.PublicKeyAlgorithmToSignatureAlgorithm[keypPair.Leaf.PublicKeyAlgorithm] 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 { if !ok {
return errors.New("unsupported certificate public key algorithm") return errors.New("unsupported certificate public key algorithm")
} }
@ -155,6 +161,11 @@ func (sd *SignedData) AddSignerInfo(keypPair tls.Certificate) (err error) {
if err != nil { if err != nil {
return err return err
} }
if !isRSAPSS(cert) {
signerOpts = hash
}
md := hash.New() md := hash.New()
if _, err = md.Write(content); err != nil { if _, err = md.Write(content); err != nil {
return err return err
@ -174,6 +185,7 @@ func (sd *SignedData) AddSignerInfo(keypPair tls.Certificate) (err error) {
return err return err
} }
si.SignedAttrs = append(si.SignedAttrs, mdAttr, ctAttr, sTAttr) si.SignedAttrs = append(si.SignedAttrs, mdAttr, ctAttr, sTAttr)
si.SignedAttrs = append(si.SignedAttrs, attrs...)
sm, err := asn.MarshalWithParams(si.SignedAttrs, `set`) sm, err := asn.MarshalWithParams(si.SignedAttrs, `set`)
if err != nil { if err != nil {
@ -184,7 +196,7 @@ func (sd *SignedData) AddSignerInfo(keypPair tls.Certificate) (err error) {
if _, errr := smd.Write(sm); errr != nil { if _, errr := smd.Write(sm); errr != nil {
return errr 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 return err
} }
@ -384,7 +396,11 @@ func (sd *SignedData) Verify(Opts x509.VerifyOptions, detached []byte) (chains [
if err != nil { if err != nil {
return 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 { if err != nil {
return return
} }

View File

@ -29,6 +29,7 @@ var (
// Signature Algorithm OIDs // Signature Algorithm OIDs
var ( var (
SignatureAlgorithmRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} SignatureAlgorithmRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
SignatureAlgorithmRSASSAPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10}
SignatureAlgorithmECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1} SignatureAlgorithmECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
SignatureAlgorithmECDSAwithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1} SignatureAlgorithmECDSAwithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
SignatureAlgorithmECDSAwithSHA224 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 1} SignatureAlgorithmECDSAwithSHA224 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 1}
@ -39,7 +40,8 @@ var (
// Public Key Encryption OIDs // Public Key Encryption OIDs
var ( var (
EncryptionAlgorithmRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} EncryptionAlgorithmRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
EncryptionAlgorithmRSAESOAEP = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 7}
) )
// Digest Algorithm OIDs // Digest Algorithm OIDs
@ -133,6 +135,11 @@ var SignatureAlgorithms = map[string]map[string]x509.SignatureAlgorithm{
DigestAlgorithmSHA384.String(): x509.SHA384WithRSA, DigestAlgorithmSHA384.String(): x509.SHA384WithRSA,
DigestAlgorithmSHA512.String(): x509.SHA512WithRSA, DigestAlgorithmSHA512.String(): x509.SHA512WithRSA,
}, },
SignatureAlgorithmRSASSAPSS.String(): map[string]x509.SignatureAlgorithm{
DigestAlgorithmSHA256.String(): x509.SHA256WithRSAPSS,
DigestAlgorithmSHA384.String(): x509.SHA384WithRSAPSS,
DigestAlgorithmSHA512.String(): x509.SHA512WithRSAPSS,
},
SignatureAlgorithmECDSA.String(): map[string]x509.SignatureAlgorithm{ SignatureAlgorithmECDSA.String(): map[string]x509.SignatureAlgorithm{
DigestAlgorithmSHA1.String(): x509.ECDSAWithSHA1, DigestAlgorithmSHA1.String(): x509.ECDSAWithSHA1,
DigestAlgorithmSHA256.String(): x509.ECDSAWithSHA256, DigestAlgorithmSHA256.String(): x509.ECDSAWithSHA256,

View File

@ -25,8 +25,15 @@ func Encrypt(in []byte, cert *x509.Certificate, opts ...string) (der []byte, err
pem.Encode(tmpKey, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}) pem.Encode(tmpKey, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
param := []string{SMIME, "-encrypt", "-aes128"} param := []string{SMIME, "-encrypt", "-aes128"}
param = append(param, opts...) if SMIME == "smime" {
param = append(param, tmpKey.Name()) // For smime arguments can not be passed after the keyfile
param = append(param, opts...)
param = append(param, tmpKey.Name())
} else {
// Keyots have to be passed after the key
param = append(param, "-recip", tmpKey.Name())
param = append(param, opts...)
}
der, err = openssl(in, param...) der, err = openssl(in, param...)
return return
@ -76,9 +83,8 @@ func SignDetached(in []byte, cert *x509.Certificate, key crypto.PrivateKey, inte
pem.Encode(tmpInterm, &pem.Block{Type: "CERTIFICATE", Bytes: i.Raw}) pem.Encode(tmpInterm, &pem.Block{Type: "CERTIFICATE", Bytes: i.Raw})
} }
param := []string{SMIME, "-sign", "-nodetach"} param := []string{SMIME, "-sign", "-nodetach", "-signer", tmpCert.Name(), "-inkey", tmpKey.Name(), "-certfile", tmpInterm.Name()}
param = append(param, opts...) param = append(param, opts...)
param = append(param, []string{"-signer", tmpCert.Name(), "-inkey", tmpKey.Name(), "-certfile", tmpInterm.Name()}...)
plain, err = openssl(in, param...) plain, err = openssl(in, param...)
return return

View File

@ -22,6 +22,7 @@ type configuration struct {
subject *pkix.Name subject *pkix.Name
issuer *Identity issuer *Identity
nextSN *int64 nextSN *int64
signatureAlgrotim x509.SignatureAlgorithm
priv *crypto.Signer priv *crypto.Signer
isCA bool isCA bool
notBefore *time.Time notBefore *time.Time
@ -33,6 +34,7 @@ type configuration struct {
func (c *configuration) generate() *Identity { func (c *configuration) generate() *Identity {
templ := &x509.Certificate{ templ := &x509.Certificate{
Subject: c.getSubject(), Subject: c.getSubject(),
SignatureAlgorithm: c.signatureAlgrotim,
IsCA: c.isCA, IsCA: c.isCA,
BasicConstraintsValid: true, BasicConstraintsValid: true,
NotAfter: c.getNotAfter(), NotAfter: c.getNotAfter(),
@ -195,6 +197,13 @@ func PrivateKey(value crypto.Signer) Option {
} }
} }
// SignatureAlgorithm is an Option for setting the signature algorithm.
func SignatureAlgorithm(value x509.SignatureAlgorithm) Option {
return func(c *configuration) {
c.signatureAlgrotim = value
}
}
// Issuer is an Option for setting the identity's issuer. // Issuer is an Option for setting the identity's issuer.
func Issuer(value *Identity) Option { func Issuer(value *Identity) Option {
return func(c *configuration) { return func(c *configuration) {

View File

@ -6,11 +6,15 @@ package smime
import ( import (
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/base64" "encoding/base64"
"errors" "errors"
"log" "log"
"strings" "strings"
"github.com/InfiniteLoopSpace/go_S-MIME/oid"
"github.com/InfiniteLoopSpace/go_S-MIME/b64" "github.com/InfiniteLoopSpace/go_S-MIME/b64"
cms "github.com/InfiniteLoopSpace/go_S-MIME/cms" cms "github.com/InfiniteLoopSpace/go_S-MIME/cms"
@ -23,6 +27,26 @@ type SMIME struct {
CMS *cms.CMS CMS *cms.CMS
} }
var oidsmimeCapabilities = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 15}
//SMIMECapability ::= SEQUENCE {
//capabilityID OBJECT IDENTIFIER,
//parameters ANY DEFINED BY capabilityID OPTIONAL }
//SMIMECapabilities ::= SEQUENCE OF SMIMECapability
func (smime *SMIME) addSMIMECapabilitesAttr() (err error) {
var smimeCapabilities []pkix.AlgorithmIdentifier
smimeCapabilities = append(smimeCapabilities, pkix.AlgorithmIdentifier{Algorithm: oid.EncryptionAlgorithmAES128CBC})
smimeCapabilities = append(smimeCapabilities, pkix.AlgorithmIdentifier{Algorithm: oid.AEADChaCha20Poly1305})
err = smime.CMS.AddAttribute(oidsmimeCapabilities, smimeCapabilities)
return
}
// New create a new instance of SMIME with given keyPairs. // New create a new instance of SMIME with given keyPairs.
func New(keyPair ...tls.Certificate) (smime *SMIME, err error) { func New(keyPair ...tls.Certificate) (smime *SMIME, err error) {
CMS, err := cms.New(keyPair...) CMS, err := cms.New(keyPair...)
@ -31,6 +55,10 @@ func New(keyPair ...tls.Certificate) (smime *SMIME, err error) {
} }
smime = &SMIME{CMS} smime = &SMIME{CMS}
err = smime.addSMIMECapabilitesAttr()
if err != nil {
return
}
return return
} }