diff --git a/msg.go b/msg.go
index 455ba3b..60f758f 100644
--- a/msg.go
+++ b/msg.go
@@ -7,7 +7,8 @@ package mail
import (
"bytes"
"context"
- "crypto/tls"
+ "crypto/rsa"
+ "crypto/x509"
"embed"
"errors"
"fmt"
@@ -338,8 +339,8 @@ func WithNoDefaultUserAgent() MsgOption {
}
// SignWithSMime configures the Msg to be signed with S/MIME
-func (m *Msg) SignWithSMime(keyPair *tls.Certificate) error {
- sMime, err := newSMime(keyPair)
+func (m *Msg) SignWithSMime(privateKey *rsa.PrivateKey, certificate *x509.Certificate, intermediateCertificate *x509.Certificate) error {
+ sMime, err := newSMime(privateKey, certificate, intermediateCertificate)
if err != nil {
return err
}
diff --git a/msg_test.go b/msg_test.go
index ffd1357..7842cda 100644
--- a/msg_test.go
+++ b/msg_test.go
@@ -1909,15 +1909,15 @@ func TestMsg_hasAlt(t *testing.T) {
// TestMsg_hasAlt tests the hasAlt() method of the Msg with active S/MIME
func TestMsg_hasAltWithSMime(t *testing.T) {
- keyPair, err := getDummyCertificate()
+ privateKey, certificate, intermediateCertificate, err := getDummyCryptoMaterial()
if err != nil {
- t.Errorf("failed to load dummy certificate. Cause: %v", err)
+ t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
}
m := NewMsg()
m.SetBodyString(TypeTextPlain, "Plain")
m.AddAlternativeString(TypeTextHTML, "HTML")
- if err := m.SignWithSMime(keyPair); err != nil {
- t.Errorf("set of certificate was not successful")
+ if err := m.SignWithSMime(privateKey, certificate, intermediateCertificate); err != nil {
+ t.Errorf("failed to init smime configuration")
}
if m.hasAlt() {
t.Errorf("mail has alternative parts and S/MIME is active, but hasAlt() returned true")
@@ -1926,13 +1926,13 @@ func TestMsg_hasAltWithSMime(t *testing.T) {
// TestMsg_hasSMime tests the hasSMime() method of the Msg
func TestMsg_hasSMime(t *testing.T) {
- keyPair, err := getDummyCertificate()
+ privateKey, certificate, intermediateCertificate, err := getDummyCryptoMaterial()
if err != nil {
- t.Errorf("failed to load dummy certificate. Cause: %v", err)
+ t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
}
m := NewMsg()
- if err := m.SignWithSMime(keyPair); err != nil {
- t.Errorf("set of certificate was not successful")
+ if err := m.SignWithSMime(privateKey, certificate, intermediateCertificate); err != nil {
+ t.Errorf("failed to init smime configuration")
}
m.SetBodyString(TypeTextPlain, "Plain")
if !m.hasSMime() {
@@ -2009,16 +2009,16 @@ func TestMsg_WriteToSkipMiddleware(t *testing.T) {
// TestMsg_WriteToWithSMIME tests the WriteTo() method of the Msg
func TestMsg_WriteToWithSMIME(t *testing.T) {
- keyPair, err := getDummyCertificate()
+ privateKey, certificate, intermediateCertificate, err := getDummyCryptoMaterial()
if err != nil {
- t.Errorf("failed to load dummy certificate. Cause: %v", err)
+ t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
}
m := NewMsg()
m.Subject("This is a test")
m.SetBodyString(TypeTextPlain, "Plain")
- if err := m.SignWithSMime(keyPair); err != nil {
- t.Errorf("set of certificate was not successful")
+ if err := m.SignWithSMime(privateKey, certificate, intermediateCertificate); err != nil {
+ t.Errorf("failed to init smime configuration")
}
wbuf := bytes.Buffer{}
@@ -3345,12 +3345,12 @@ func TestNewMsgWithNoDefaultUserAgent(t *testing.T) {
// TestSignWithSMime_ValidKeyPair tests WithSMimeSinging with given key pair
func TestSignWithSMime_ValidKeyPair(t *testing.T) {
- keyPair, err := getDummyCertificate()
+ privateKey, certificate, intermediateCertificate, err := getDummyCryptoMaterial()
if err != nil {
- t.Errorf("failed to load dummy certificate. Cause: %v", err)
+ t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
}
m := NewMsg()
- if err := m.SignWithSMime(keyPair); err != nil {
+ if err := m.SignWithSMime(privateKey, certificate, intermediateCertificate); err != nil {
t.Errorf("failed to set sMime. Cause: %v", err)
}
if m.sMime.privateKey == nil {
@@ -3361,13 +3361,41 @@ func TestSignWithSMime_ValidKeyPair(t *testing.T) {
}
}
-// TestSignWithSMime_InvalidKeyPair tests WithSMimeSinging with given invalid key pair
-func TestSignWithSMime_InvalidKeyPair(t *testing.T) {
+// TestSignWithSMime_InvalidPrivateKey tests WithSMimeSinging with given invalid private key
+func TestSignWithSMime_InvalidPrivateKey(t *testing.T) {
m := NewMsg()
- err := m.SignWithSMime(nil)
- if !errors.Is(err, ErrInvalidKeyPair) {
- t.Errorf("failed to check sMimeAuthConfig values correctly: %s", err)
+ err := m.SignWithSMime(nil, nil, nil)
+ if !errors.Is(err, ErrInvalidPrivateKey) {
+ t.Errorf("failed to pre-check SignWithSMime method values correctly: %s", err)
+ }
+}
+
+// TestSignWithSMime_InvalidCertificate tests WithSMimeSinging with given invalid certificate
+func TestSignWithSMime_InvalidCertificate(t *testing.T) {
+ privateKey, _, _, err := getDummyCryptoMaterial()
+ if err != nil {
+ t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
+ }
+ m := NewMsg()
+
+ err = m.SignWithSMime(privateKey, nil, nil)
+ if !errors.Is(err, ErrInvalidCertificate) {
+ t.Errorf("failed to pre-check SignWithSMime method values correctly: %s", err)
+ }
+}
+
+// TestSignWithSMime_InvalidIntermediateCertificate tests WithSMimeSinging with given invalid intermediate certificate
+func TestSignWithSMime_InvalidIntermediateCertificate(t *testing.T) {
+ privateKey, certificate, _, err := getDummyCryptoMaterial()
+ if err != nil {
+ t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
+ }
+ m := NewMsg()
+
+ err = m.SignWithSMime(privateKey, certificate, nil)
+ if !errors.Is(err, ErrInvalidIntermediateCertificate) {
+ t.Errorf("failed to pre-check SignWithSMime method values correctly: %s", err)
}
}
@@ -3401,13 +3429,13 @@ func FuzzMsg_From(f *testing.F) {
// TestMsg_createSignaturePart tests the Msg.createSignaturePart method
func TestMsg_createSignaturePart(t *testing.T) {
- keyPair, err := getDummyCertificate()
+ privateKey, certificate, intermediateCertificate, err := getDummyCryptoMaterial()
if err != nil {
- t.Errorf("failed to load dummy certificate. Cause: %v", err)
+ t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
}
m := NewMsg()
- if err := m.SignWithSMime(keyPair); err != nil {
- t.Errorf("set of certificate was not successful")
+ if err := m.SignWithSMime(privateKey, certificate, intermediateCertificate); err != nil {
+ t.Errorf("failed to init smime configuration")
}
body := []byte("This is the body")
part, err := m.createSignaturePart(EncodingQP, TypeTextPlain, CharsetUTF7, body)
@@ -3431,15 +3459,15 @@ func TestMsg_createSignaturePart(t *testing.T) {
// TestMsg_signMessage tests the Msg.signMessage method
func TestMsg_signMessage(t *testing.T) {
- keyPair, err := getDummyCertificate()
+ privateKey, certificate, intermediateCertificate, err := getDummyCryptoMaterial()
if err != nil {
- t.Errorf("failed to load dummy certificate. Cause: %v", err)
+ t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
}
body := []byte("This is the body")
m := NewMsg()
m.SetBodyString(TypeTextPlain, string(body))
- if err := m.SignWithSMime(keyPair); err != nil {
- t.Errorf("set of certificate was not successful")
+ if err := m.SignWithSMime(privateKey, certificate, intermediateCertificate); err != nil {
+ t.Errorf("failed to init smime configuration")
}
msg, err := m.signMessage(m)
if err != nil {
diff --git a/msgwriter_test.go b/msgwriter_test.go
index 15e751f..6e36c4b 100644
--- a/msgwriter_test.go
+++ b/msgwriter_test.go
@@ -157,14 +157,14 @@ func TestMsgWriter_writeMsg_PGP(t *testing.T) {
// TestMsgWriter_writeMsg_SMime tests the writeMsg method of the msgWriter with S/MIME types set
func TestMsgWriter_writeMsg_SMime(t *testing.T) {
- keyPair, err := getDummyCertificate()
+ privateKey, certificate, intermediateCertificate, err := getDummyCryptoMaterial()
if err != nil {
- t.Errorf("failed to load dummy certificate. Cause: %v", err)
+ t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
}
m := NewMsg()
- if err := m.SignWithSMime(keyPair); err != nil {
- t.Errorf("set of certificate was not successful")
+ if err := m.SignWithSMime(privateKey, certificate, intermediateCertificate); err != nil {
+ t.Errorf("failed to init smime configuration")
}
_ = m.From(`"Toni Tester" `)
_ = m.To(`"Toni Receiver" `)
diff --git a/smime.go b/smime.go
index ae6e4f2..37fb536 100644
--- a/smime.go
+++ b/smime.go
@@ -3,7 +3,6 @@ package mail
import (
"bytes"
"crypto/rsa"
- "crypto/tls"
"crypto/x509"
"encoding/pem"
"errors"
@@ -13,11 +12,14 @@ import (
)
var (
- // ErrInvalidKeyPair should be used if key pair is invalid
- ErrInvalidKeyPair = errors.New("invalid key pair")
+ // ErrInvalidPrivateKey should be used if private key is invalid
+ ErrInvalidPrivateKey = errors.New("invalid private key")
- // ErrInvalidParentCertificates should be used if one of the parent certificates is invalid
- ErrInvalidParentCertificates = errors.New("invalid parent certificates")
+ // ErrInvalidCertificate should be used if the certificate is invalid
+ ErrInvalidCertificate = errors.New("invalid certificate")
+
+ // ErrInvalidIntermediateCertificate should be used if the intermediate certificate is invalid
+ ErrInvalidIntermediateCertificate = errors.New("invalid intermediate certificate")
// ErrCouldNotInitialize should be used if the signed data could not initialize
ErrCouldNotInitialize = errors.New("could not initialize signed data")
@@ -34,30 +36,32 @@ var (
// SMime is used to sign messages with S/MIME
type SMime struct {
- privateKey *rsa.PrivateKey
- parentCertificates []*x509.Certificate
- certificate *x509.Certificate
+ privateKey *rsa.PrivateKey
+ certificate *x509.Certificate
+ intermediateCertificate *x509.Certificate
}
-// NewSMime construct a new instance of SMime with a provided *tls.Certificate
-func newSMime(keyPair *tls.Certificate) (*SMime, error) {
- if keyPair == nil {
- return nil, ErrInvalidKeyPair
+// NewSMime construct a new instance of SMime with provided parameters
+// privateKey as *rsa.PrivateKey
+// certificate as *x509.Certificate
+// intermediateCertificate as *x509.Certificate
+func newSMime(privateKey *rsa.PrivateKey, certificate *x509.Certificate, intermediateCertificate *x509.Certificate) (*SMime, error) {
+ if privateKey == nil {
+ return nil, ErrInvalidPrivateKey
}
- parentCertificates := make([]*x509.Certificate, 0)
- for _, cert := range keyPair.Certificate[1:] {
- c, err := x509.ParseCertificate(cert)
- if err != nil {
- return nil, ErrInvalidParentCertificates
- }
- parentCertificates = append(parentCertificates, c)
+ if certificate == nil {
+ return nil, ErrInvalidCertificate
+ }
+
+ if intermediateCertificate == nil {
+ return nil, ErrInvalidIntermediateCertificate
}
return &SMime{
- privateKey: keyPair.PrivateKey.(*rsa.PrivateKey),
- certificate: keyPair.Leaf,
- parentCertificates: parentCertificates,
+ privateKey: privateKey,
+ certificate: certificate,
+ intermediateCertificate: intermediateCertificate,
}, nil
}
@@ -72,7 +76,7 @@ func (sm *SMime) signMessage(message string) (*string, error) {
return nil, ErrCouldNotInitialize
}
- if err = signedData.AddSignerChain(sm.certificate, sm.privateKey, sm.parentCertificates, pkcs7.SignerInfoConfig{}); err != nil {
+ if err = signedData.AddSignerChain(sm.certificate, sm.privateKey, []*x509.Certificate{sm.intermediateCertificate}, pkcs7.SignerInfoConfig{}); err != nil {
return nil, ErrCouldNotAddSigner
}
diff --git a/smime_test.go b/smime_test.go
index cfd8518..bf4e2d0 100644
--- a/smime_test.go
+++ b/smime_test.go
@@ -10,32 +10,35 @@ import (
// TestNewSMime tests the newSMime method
func TestNewSMime(t *testing.T) {
- keyPair, err := getDummyCertificate()
+ privateKey, certificate, intermediateCertificate, err := getDummyCryptoMaterial()
if err != nil {
- t.Errorf("Error getting dummy certificate: %s", err)
+ t.Errorf("Error getting dummy crypto material: %s", err)
}
- sMime, err := newSMime(keyPair)
+ sMime, err := newSMime(privateKey, certificate, intermediateCertificate)
if err != nil {
t.Errorf("Error creating new SMime from keyPair: %s", err)
}
- if sMime.privateKey != keyPair.PrivateKey {
+ if sMime.privateKey != privateKey {
t.Errorf("NewSMime() did not return the same private key")
}
- if sMime.certificate != keyPair.Leaf {
- t.Errorf("NewSMime() did not return the same leaf certificate")
+ if sMime.certificate != certificate {
+ t.Errorf("NewSMime() did not return the same certificate")
+ }
+ if sMime.intermediateCertificate != intermediateCertificate {
+ t.Errorf("NewSMime() did not return the same intermedidate certificate")
}
}
// TestSign tests the sign method
func TestSign(t *testing.T) {
- keyPair, err := getDummyCertificate()
+ privateKey, certificate, intermediateCertificate, err := getDummyCryptoMaterial()
if err != nil {
- t.Errorf("Error getting dummy certificate: %s", err)
+ t.Errorf("Error getting dummy crypto material: %s", err)
}
- sMime, err := newSMime(keyPair)
+ sMime, err := newSMime(privateKey, certificate, intermediateCertificate)
if err != nil {
t.Errorf("Error creating new SMime from keyPair: %s", err)
}
@@ -53,12 +56,12 @@ func TestSign(t *testing.T) {
// TestPrepareMessage tests the createMessage method
func TestPrepareMessage(t *testing.T) {
- keyPair, err := getDummyCertificate()
+ privateKey, certificate, intermediateCertificate, err := getDummyCryptoMaterial()
if err != nil {
- t.Errorf("Error getting dummy certificate: %s", err)
+ t.Errorf("Error getting dummy crypto material: %s", err)
}
- sMime, err := newSMime(keyPair)
+ sMime, err := newSMime(privateKey, certificate, intermediateCertificate)
if err != nil {
t.Errorf("Error creating new SMime from keyPair: %s", err)
}
diff --git a/util_test.go b/util_test.go
index 7c54b7d..33615d8 100644
--- a/util_test.go
+++ b/util_test.go
@@ -1,6 +1,7 @@
package mail
import (
+ "crypto/rsa"
"crypto/tls"
"crypto/x509"
)
@@ -10,14 +11,24 @@ const (
keyFilePath = "dummy-child-key.pem"
)
-// getDummyCertificate loads a certificate and a private key form local disk for testing purposes
-func getDummyCertificate() (*tls.Certificate, error) {
+// getDummyCryptoMaterial loads a certificate and a private key form local disk for testing purposes
+func getDummyCryptoMaterial() (*rsa.PrivateKey, *x509.Certificate, *x509.Certificate, error) {
keyPair, err := tls.LoadX509KeyPair(certFilePath, keyFilePath)
if err != nil {
- return nil, err
+ return nil, nil, nil, err
}
- keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0])
+ privateKey := keyPair.PrivateKey.(*rsa.PrivateKey)
- return &keyPair, nil
+ certificate, err := x509.ParseCertificate(keyPair.Certificate[0])
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ intermediateCertificate, err := x509.ParseCertificate(keyPair.Certificate[1])
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ return privateKey, certificate, intermediateCertificate, nil
}