Added support for RSASSA-PSS and RSAES-OAEP.
This commit is contained in:
parent
5f34d82562
commit
3f58f9a4b2
@ -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)
|
||||
- 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
|
||||
- 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 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
|
||||
|
||||
@ -53,8 +55,6 @@ plaintext, _ := SMIME.Verify(signedMsg)
|
||||
|
||||
## Todo
|
||||
|
||||
- Add S/MIME capabilities attributes
|
||||
- Add ECDH for encryption and decryption
|
||||
- Testing
|
||||
|
||||
|
||||
|
19
cms/cms.go
19
cms/cms.go
@ -23,6 +23,7 @@ type CMS struct {
|
||||
TimeStampServer string
|
||||
TimeStamp bool
|
||||
keyPairs []tls.Certificate
|
||||
signedAttrs []protocol.Attribute
|
||||
}
|
||||
|
||||
// New create a new instance of CMS with given keyPairs.
|
||||
@ -54,6 +55,19 @@ func New(cert ...tls.Certificate) (cms *CMS, err error) {
|
||||
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.
|
||||
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 {
|
||||
sd.AddSignerInfo(cms.keyPairs[i])
|
||||
err = sd.AddSignerInfo(cms.keyPairs[i], cms.signedAttrs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if cms.TimeStamp {
|
||||
|
294
cms/cms_test.go
294
cms/cms_test.go
@ -29,216 +29,250 @@ var (
|
||||
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)
|
||||
|
||||
leafECC = intermediate.Issue(pki.Subject(pkix.Name{
|
||||
CommonName: "leaf.example.com",
|
||||
}), pki.PrivateKey(ecKey))
|
||||
|
||||
keyPair = tls.Certificate{
|
||||
keyPairRSA = tls.Certificate{
|
||||
Certificate: [][]byte{leaf.Certificate.Raw, intermediate.Certificate.Raw, root.Certificate.Raw},
|
||||
PrivateKey: leaf.PrivateKey.(crypto.PrivateKey),
|
||||
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{
|
||||
Certificate: [][]byte{leafECC.Certificate.Raw, intermediate.Certificate.Raw, root.Certificate.Raw},
|
||||
PrivateKey: leafECC.PrivateKey.(crypto.PrivateKey),
|
||||
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) {
|
||||
|
||||
keyPairs = []tls.Certificate{keyPair, keyPairECC}
|
||||
// Test RSA
|
||||
keypair = keyPairRSA
|
||||
|
||||
version, err := openssl.Openssl(nil, "version")
|
||||
|
||||
if err == nil {
|
||||
keyPairsOpenssl = append(keyPairsOpenssl, keyPair)
|
||||
if err != nil {
|
||||
skipOpenssl = true
|
||||
}
|
||||
|
||||
m.Run()
|
||||
|
||||
// Test RSA PSS OAEP
|
||||
keypair = keyPairRSAPSS
|
||||
|
||||
if strings.HasPrefix(string(version), "OpenSSL 1.1") {
|
||||
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()
|
||||
}
|
||||
|
||||
func TestAuthEnrypt(t *testing.T) {
|
||||
|
||||
for _, keypair := range keyPairs {
|
||||
cms, err := New(keypair)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
cms, err := New(keypair)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
plaintext := []byte("Hallo Welt!")
|
||||
plaintext := []byte("Hallo Welt!")
|
||||
|
||||
ciphertext, err := cms.AuthEncrypt(plaintext, []*x509.Certificate{keypair.Leaf})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
ciphertext, err := cms.AuthEncrypt(plaintext, []*x509.Certificate{keypair.Leaf})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
plain, err := cms.AuthDecrypt(ciphertext)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
plain, err := cms.AuthDecrypt(ciphertext)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(plaintext, plain) {
|
||||
t.Fatal("Encryption and decryption are not inverse")
|
||||
}
|
||||
if !bytes.Equal(plaintext, plain) {
|
||||
t.Fatal("Encryption and decryption are not inverse")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnryptDecrypt(t *testing.T) {
|
||||
|
||||
for _, keypair := range keyPairs {
|
||||
cms, err := New(keypair)
|
||||
if err != nil {
|
||||
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")
|
||||
}
|
||||
cms, err := New(keypair)
|
||||
if err != nil {
|
||||
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")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestSignVerify(t *testing.T) {
|
||||
|
||||
for _, keypair := range keyPairs {
|
||||
cms, err := New(keypair)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
cms, err := New(keypair)
|
||||
if err != nil {
|
||||
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)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
der, err := cms.Sign(msg)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, err = cms.Verify(der)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
_, err = cms.Verify(der)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncryptOpenSSL(t *testing.T) {
|
||||
|
||||
for _, keypair := range keyPairsOpenssl {
|
||||
message := []byte("Hallo Welt!")
|
||||
|
||||
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")
|
||||
}
|
||||
if skipOpenssl {
|
||||
return
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
for _, keypair := range keyPairsOpenssl {
|
||||
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")
|
||||
}
|
||||
if skipOpenssl {
|
||||
return
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
for _, keypair := range keyPairsOpenssl {
|
||||
message := []byte("Hallo Welt")
|
||||
if skipOpenssl {
|
||||
return
|
||||
}
|
||||
|
||||
sig, err := openssl.SignDetached(message, keypair.Leaf, keypair.PrivateKey, []*x509.Certificate{intermediate.Certificate}, "-outform", "DER")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
message := []byte("Hallo Welt")
|
||||
|
||||
cms, err := New()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
cms.roots.AddCert(root.Certificate)
|
||||
sig, err := openssl.SignDetached(message, keypair.Leaf, keypair.PrivateKey, []*x509.Certificate{intermediate.Certificate}, opensslSignOpts...)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, err = cms.Verify(sig)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
cms, err := New()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
cms.roots.AddCert(root.Certificate)
|
||||
|
||||
_, err = cms.Verify(sig)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyOpenSSL(t *testing.T) {
|
||||
|
||||
for _, keypair := range keyPairsOpenssl {
|
||||
cms, err := New(keypair)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if skipOpenssl {
|
||||
return
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
msg := []byte("Hallo Welt!")
|
||||
|
||||
sig, err := openssl.Verify(der, root.Certificate, "-inform", "DER")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
der, err := cms.Sign(msg)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(msg, sig) {
|
||||
t.Fatal("Signed message and message do not agree!")
|
||||
}
|
||||
sig, err := openssl.Verify(der, root.Certificate, "-inform", "DER")
|
||||
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
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
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ var (
|
||||
// Signature Algorithm OIDs
|
||||
var (
|
||||
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}
|
||||
SignatureAlgorithmECDSAwithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
|
||||
SignatureAlgorithmECDSAwithSHA224 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 1}
|
||||
@ -39,7 +40,8 @@ var (
|
||||
|
||||
// Public Key Encryption OIDs
|
||||
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
|
||||
@ -133,6 +135,11 @@ var SignatureAlgorithms = map[string]map[string]x509.SignatureAlgorithm{
|
||||
DigestAlgorithmSHA384.String(): x509.SHA384WithRSA,
|
||||
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{
|
||||
DigestAlgorithmSHA1.String(): x509.ECDSAWithSHA1,
|
||||
DigestAlgorithmSHA256.String(): x509.ECDSAWithSHA256,
|
||||
|
@ -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})
|
||||
|
||||
param := []string{SMIME, "-encrypt", "-aes128"}
|
||||
param = append(param, opts...)
|
||||
param = append(param, tmpKey.Name())
|
||||
if SMIME == "smime" {
|
||||
// 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...)
|
||||
|
||||
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})
|
||||
}
|
||||
|
||||
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, []string{"-signer", tmpCert.Name(), "-inkey", tmpKey.Name(), "-certfile", tmpInterm.Name()}...)
|
||||
plain, err = openssl(in, param...)
|
||||
|
||||
return
|
||||
|
@ -22,6 +22,7 @@ type configuration struct {
|
||||
subject *pkix.Name
|
||||
issuer *Identity
|
||||
nextSN *int64
|
||||
signatureAlgrotim x509.SignatureAlgorithm
|
||||
priv *crypto.Signer
|
||||
isCA bool
|
||||
notBefore *time.Time
|
||||
@ -33,6 +34,7 @@ type configuration struct {
|
||||
func (c *configuration) generate() *Identity {
|
||||
templ := &x509.Certificate{
|
||||
Subject: c.getSubject(),
|
||||
SignatureAlgorithm: c.signatureAlgrotim,
|
||||
IsCA: c.isCA,
|
||||
BasicConstraintsValid: true,
|
||||
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.
|
||||
func Issuer(value *Identity) Option {
|
||||
return func(c *configuration) {
|
||||
|
@ -6,11 +6,15 @@ package smime
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/InfiniteLoopSpace/go_S-MIME/oid"
|
||||
|
||||
"github.com/InfiniteLoopSpace/go_S-MIME/b64"
|
||||
|
||||
cms "github.com/InfiniteLoopSpace/go_S-MIME/cms"
|
||||
@ -23,6 +27,26 @@ type SMIME struct {
|
||||
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.
|
||||
func New(keyPair ...tls.Certificate) (smime *SMIME, err error) {
|
||||
CMS, err := cms.New(keyPair...)
|
||||
@ -31,6 +55,10 @@ func New(keyPair ...tls.Certificate) (smime *SMIME, err error) {
|
||||
}
|
||||
|
||||
smime = &SMIME{CMS}
|
||||
err = smime.addSMIMECapabilitesAttr()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user