mirror of
https://github.com/wneessen/go-mail.git
synced 2024-11-22 13:50:49 +01:00
chore: formatting and using another types as deprecated ones
This commit is contained in:
parent
978a01eaf6
commit
4a3dbddaff
7 changed files with 224 additions and 98 deletions
42
msg.go
42
msg.go
|
@ -7,7 +7,9 @@ package mail
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"embed"
|
"embed"
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -338,9 +340,9 @@ func WithNoDefaultUserAgent() MsgOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignWithSMime configures the Msg to be signed with S/MIME
|
// SignWithSMimeRSA configures the Msg to be signed with S/MIME
|
||||||
func (m *Msg) SignWithSMime(privateKey *rsa.PrivateKey, certificate *x509.Certificate, intermediateCertificate *x509.Certificate) error {
|
func (m *Msg) SignWithSMimeRSA(privateKey *rsa.PrivateKey, certificate *x509.Certificate, intermediateCertificate *x509.Certificate) error {
|
||||||
sMime, err := newSMime(privateKey, certificate, intermediateCertificate)
|
sMime, err := newSMimeWithRSA(privateKey, certificate, intermediateCertificate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -348,6 +350,40 @@ func (m *Msg) SignWithSMime(privateKey *rsa.PrivateKey, certificate *x509.Certif
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SignWithSMimeECDSA configures the Msg to be signed with S/MIME
|
||||||
|
func (m *Msg) SignWithSMimeECDSA(privateKey *ecdsa.PrivateKey, certificate *x509.Certificate, intermediateCertificate *x509.Certificate) error {
|
||||||
|
sMime, err := newSMimeWithECDSA(privateKey, certificate, intermediateCertificate)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.sMime = sMime
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignWithTLSCertificate signs the Msg with the provided *tls.certificate.
|
||||||
|
func (m *Msg) SignWithTLSCertificate(keyPairTlS *tls.Certificate) error {
|
||||||
|
intermediateCertificate, err := x509.ParseCertificate(keyPairTlS.Certificate[1])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse intermediate certificate: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch keyPairTlS.PrivateKey.(type) {
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
if intermediateCertificate == nil {
|
||||||
|
return m.SignWithSMimeRSA(keyPairTlS.PrivateKey.(*rsa.PrivateKey), keyPairTlS.Leaf, nil)
|
||||||
|
}
|
||||||
|
return m.SignWithSMimeRSA(keyPairTlS.PrivateKey.(*rsa.PrivateKey), keyPairTlS.Leaf, intermediateCertificate)
|
||||||
|
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
if intermediateCertificate == nil {
|
||||||
|
return m.SignWithSMimeECDSA(keyPairTlS.PrivateKey.(*ecdsa.PrivateKey), keyPairTlS.Leaf, nil)
|
||||||
|
}
|
||||||
|
return m.SignWithSMimeECDSA(keyPairTlS.PrivateKey.(*ecdsa.PrivateKey), keyPairTlS.Leaf, intermediateCertificate)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported private key type: %T", keyPairTlS.PrivateKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This method allows you to specify a character set for the email message. The charset is
|
// This method allows you to specify a character set for the email message. The charset is
|
||||||
// important for ensuring that the content of the message is correctly interpreted by
|
// important for ensuring that the content of the message is correctly interpreted by
|
||||||
// mail clients. Common charset values include UTF-8, ISO-8859-1, and others. If a charset
|
// mail clients. Common charset values include UTF-8, ISO-8859-1, and others. If a charset
|
||||||
|
|
68
msg_test.go
68
msg_test.go
|
@ -1909,14 +1909,14 @@ func TestMsg_hasAlt(t *testing.T) {
|
||||||
|
|
||||||
// TestMsg_hasAlt tests the hasAlt() method of the Msg with active S/MIME
|
// TestMsg_hasAlt tests the hasAlt() method of the Msg with active S/MIME
|
||||||
func TestMsg_hasAltWithSMime(t *testing.T) {
|
func TestMsg_hasAltWithSMime(t *testing.T) {
|
||||||
privateKey, certificate, intermediateCertificate, err := getDummyCryptoMaterial()
|
privateKey, certificate, intermediateCertificate, err := getDummyRSACryptoMaterial()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
|
t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
|
||||||
}
|
}
|
||||||
m := NewMsg()
|
m := NewMsg()
|
||||||
m.SetBodyString(TypeTextPlain, "Plain")
|
m.SetBodyString(TypeTextPlain, "Plain")
|
||||||
m.AddAlternativeString(TypeTextHTML, "<b>HTML</b>")
|
m.AddAlternativeString(TypeTextHTML, "<b>HTML</b>")
|
||||||
if err := m.SignWithSMime(privateKey, certificate, intermediateCertificate); err != nil {
|
if err := m.SignWithSMimeRSA(privateKey, certificate, intermediateCertificate); err != nil {
|
||||||
t.Errorf("failed to init smime configuration")
|
t.Errorf("failed to init smime configuration")
|
||||||
}
|
}
|
||||||
if m.hasAlt() {
|
if m.hasAlt() {
|
||||||
|
@ -1926,12 +1926,12 @@ func TestMsg_hasAltWithSMime(t *testing.T) {
|
||||||
|
|
||||||
// TestMsg_hasSMime tests the hasSMime() method of the Msg
|
// TestMsg_hasSMime tests the hasSMime() method of the Msg
|
||||||
func TestMsg_hasSMime(t *testing.T) {
|
func TestMsg_hasSMime(t *testing.T) {
|
||||||
privateKey, certificate, intermediateCertificate, err := getDummyCryptoMaterial()
|
privateKey, certificate, intermediateCertificate, err := getDummyRSACryptoMaterial()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
|
t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
|
||||||
}
|
}
|
||||||
m := NewMsg()
|
m := NewMsg()
|
||||||
if err := m.SignWithSMime(privateKey, certificate, intermediateCertificate); err != nil {
|
if err := m.SignWithSMimeRSA(privateKey, certificate, intermediateCertificate); err != nil {
|
||||||
t.Errorf("failed to init smime configuration")
|
t.Errorf("failed to init smime configuration")
|
||||||
}
|
}
|
||||||
m.SetBodyString(TypeTextPlain, "Plain")
|
m.SetBodyString(TypeTextPlain, "Plain")
|
||||||
|
@ -2009,7 +2009,7 @@ func TestMsg_WriteToSkipMiddleware(t *testing.T) {
|
||||||
|
|
||||||
// TestMsg_WriteToWithSMIME tests the WriteTo() method of the Msg
|
// TestMsg_WriteToWithSMIME tests the WriteTo() method of the Msg
|
||||||
func TestMsg_WriteToWithSMIME(t *testing.T) {
|
func TestMsg_WriteToWithSMIME(t *testing.T) {
|
||||||
privateKey, certificate, intermediateCertificate, err := getDummyCryptoMaterial()
|
privateKey, certificate, intermediateCertificate, err := getDummyRSACryptoMaterial()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
|
t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -2017,7 +2017,7 @@ func TestMsg_WriteToWithSMIME(t *testing.T) {
|
||||||
m := NewMsg()
|
m := NewMsg()
|
||||||
m.Subject("This is a test")
|
m.Subject("This is a test")
|
||||||
m.SetBodyString(TypeTextPlain, "Plain")
|
m.SetBodyString(TypeTextPlain, "Plain")
|
||||||
if err := m.SignWithSMime(privateKey, certificate, intermediateCertificate); err != nil {
|
if err := m.SignWithSMimeRSA(privateKey, certificate, intermediateCertificate); err != nil {
|
||||||
t.Errorf("failed to init smime configuration")
|
t.Errorf("failed to init smime configuration")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3343,17 +3343,35 @@ func TestNewMsgWithNoDefaultUserAgent(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestSignWithSMime_ValidKeyPair tests WithSMimeSinging with given key pair
|
// TestSignWithSMime_ValidRSAKeyPair tests WithSMimeSinging with given rsa key pair
|
||||||
func TestSignWithSMime_ValidKeyPair(t *testing.T) {
|
func TestSignWithSMime_ValidRSAKeyPair(t *testing.T) {
|
||||||
privateKey, certificate, intermediateCertificate, err := getDummyCryptoMaterial()
|
privateKey, certificate, intermediateCertificate, err := getDummyRSACryptoMaterial()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
|
t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
|
||||||
}
|
}
|
||||||
m := NewMsg()
|
m := NewMsg()
|
||||||
if err := m.SignWithSMime(privateKey, certificate, intermediateCertificate); err != nil {
|
if err := m.SignWithSMimeRSA(privateKey, certificate, intermediateCertificate); err != nil {
|
||||||
t.Errorf("failed to set sMime. Cause: %v", err)
|
t.Errorf("failed to set sMime. Cause: %v", err)
|
||||||
}
|
}
|
||||||
if m.sMime.privateKey == nil {
|
if m.sMime.privateKey.rsa == nil {
|
||||||
|
t.Errorf("WithSMimeSinging() - no private key is given")
|
||||||
|
}
|
||||||
|
if m.sMime.certificate == nil {
|
||||||
|
t.Errorf("WithSMimeSinging() - no certificate is given")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSignWithSMime_ValidRSAKeyPair tests WithSMimeSinging with given ecdsa key pair
|
||||||
|
func TestSignWithSMime_ValidECDSAKeyPair(t *testing.T) {
|
||||||
|
privateKey, certificate, intermediateCertificate, err := getDummyECDSACryptoMaterial()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
|
||||||
|
}
|
||||||
|
m := NewMsg()
|
||||||
|
if err := m.SignWithSMimeECDSA(privateKey, certificate, intermediateCertificate); err != nil {
|
||||||
|
t.Errorf("failed to set sMime. Cause: %v", err)
|
||||||
|
}
|
||||||
|
if m.sMime.privateKey.rsa == nil {
|
||||||
t.Errorf("WithSMimeSinging() - no private key is given")
|
t.Errorf("WithSMimeSinging() - no private key is given")
|
||||||
}
|
}
|
||||||
if m.sMime.certificate == nil {
|
if m.sMime.certificate == nil {
|
||||||
|
@ -3365,7 +3383,7 @@ func TestSignWithSMime_ValidKeyPair(t *testing.T) {
|
||||||
func TestSignWithSMime_InvalidPrivateKey(t *testing.T) {
|
func TestSignWithSMime_InvalidPrivateKey(t *testing.T) {
|
||||||
m := NewMsg()
|
m := NewMsg()
|
||||||
|
|
||||||
err := m.SignWithSMime(nil, nil, nil)
|
err := m.SignWithSMimeRSA(nil, nil, nil)
|
||||||
if !errors.Is(err, ErrInvalidPrivateKey) {
|
if !errors.Is(err, ErrInvalidPrivateKey) {
|
||||||
t.Errorf("failed to pre-check SignWithSMime method values correctly: %s", err)
|
t.Errorf("failed to pre-check SignWithSMime method values correctly: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -3373,32 +3391,18 @@ func TestSignWithSMime_InvalidPrivateKey(t *testing.T) {
|
||||||
|
|
||||||
// TestSignWithSMime_InvalidCertificate tests WithSMimeSinging with given invalid certificate
|
// TestSignWithSMime_InvalidCertificate tests WithSMimeSinging with given invalid certificate
|
||||||
func TestSignWithSMime_InvalidCertificate(t *testing.T) {
|
func TestSignWithSMime_InvalidCertificate(t *testing.T) {
|
||||||
privateKey, _, _, err := getDummyCryptoMaterial()
|
privateKey, _, _, err := getDummyRSACryptoMaterial()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
|
t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
|
||||||
}
|
}
|
||||||
m := NewMsg()
|
m := NewMsg()
|
||||||
|
|
||||||
err = m.SignWithSMime(privateKey, nil, nil)
|
err = m.SignWithSMimeRSA(privateKey, nil, nil)
|
||||||
if !errors.Is(err, ErrInvalidCertificate) {
|
if !errors.Is(err, ErrInvalidCertificate) {
|
||||||
t.Errorf("failed to pre-check SignWithSMime method values correctly: %s", err)
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fuzzing tests
|
// Fuzzing tests
|
||||||
func FuzzMsg_Subject(f *testing.F) {
|
func FuzzMsg_Subject(f *testing.F) {
|
||||||
f.Add("Testsubject")
|
f.Add("Testsubject")
|
||||||
|
@ -3429,12 +3433,12 @@ func FuzzMsg_From(f *testing.F) {
|
||||||
|
|
||||||
// TestMsg_createSignaturePart tests the Msg.createSignaturePart method
|
// TestMsg_createSignaturePart tests the Msg.createSignaturePart method
|
||||||
func TestMsg_createSignaturePart(t *testing.T) {
|
func TestMsg_createSignaturePart(t *testing.T) {
|
||||||
privateKey, certificate, intermediateCertificate, err := getDummyCryptoMaterial()
|
privateKey, certificate, intermediateCertificate, err := getDummyRSACryptoMaterial()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
|
t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
|
||||||
}
|
}
|
||||||
m := NewMsg()
|
m := NewMsg()
|
||||||
if err := m.SignWithSMime(privateKey, certificate, intermediateCertificate); err != nil {
|
if err := m.SignWithSMimeRSA(privateKey, certificate, intermediateCertificate); err != nil {
|
||||||
t.Errorf("failed to init smime configuration")
|
t.Errorf("failed to init smime configuration")
|
||||||
}
|
}
|
||||||
body := []byte("This is the body")
|
body := []byte("This is the body")
|
||||||
|
@ -3459,14 +3463,14 @@ func TestMsg_createSignaturePart(t *testing.T) {
|
||||||
|
|
||||||
// TestMsg_signMessage tests the Msg.signMessage method
|
// TestMsg_signMessage tests the Msg.signMessage method
|
||||||
func TestMsg_signMessage(t *testing.T) {
|
func TestMsg_signMessage(t *testing.T) {
|
||||||
privateKey, certificate, intermediateCertificate, err := getDummyCryptoMaterial()
|
privateKey, certificate, intermediateCertificate, err := getDummyRSACryptoMaterial()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
|
t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
|
||||||
}
|
}
|
||||||
body := []byte("This is the body")
|
body := []byte("This is the body")
|
||||||
m := NewMsg()
|
m := NewMsg()
|
||||||
m.SetBodyString(TypeTextPlain, string(body))
|
m.SetBodyString(TypeTextPlain, string(body))
|
||||||
if err := m.SignWithSMime(privateKey, certificate, intermediateCertificate); err != nil {
|
if err := m.SignWithSMimeRSA(privateKey, certificate, intermediateCertificate); err != nil {
|
||||||
t.Errorf("failed to init smime configuration")
|
t.Errorf("failed to init smime configuration")
|
||||||
}
|
}
|
||||||
msg, err := m.signMessage(m)
|
msg, err := m.signMessage(m)
|
||||||
|
|
|
@ -157,13 +157,13 @@ func TestMsgWriter_writeMsg_PGP(t *testing.T) {
|
||||||
|
|
||||||
// TestMsgWriter_writeMsg_SMime tests the writeMsg method of the msgWriter with S/MIME types set
|
// TestMsgWriter_writeMsg_SMime tests the writeMsg method of the msgWriter with S/MIME types set
|
||||||
func TestMsgWriter_writeMsg_SMime(t *testing.T) {
|
func TestMsgWriter_writeMsg_SMime(t *testing.T) {
|
||||||
privateKey, certificate, intermediateCertificate, err := getDummyCryptoMaterial()
|
privateKey, certificate, intermediateCertificate, err := getDummyRSACryptoMaterial()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
|
t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
m := NewMsg()
|
m := NewMsg()
|
||||||
if err := m.SignWithSMime(privateKey, certificate, intermediateCertificate); err != nil {
|
if err := m.SignWithSMimeRSA(privateKey, certificate, intermediateCertificate); err != nil {
|
||||||
t.Errorf("failed to init smime configuration")
|
t.Errorf("failed to init smime configuration")
|
||||||
}
|
}
|
||||||
_ = m.From(`"Toni Tester" <test@example.com>`)
|
_ = m.From(`"Toni Tester" <test@example.com>`)
|
||||||
|
|
10
pkcs7.go
10
pkcs7.go
|
@ -25,7 +25,7 @@ import (
|
||||||
type PKCS7 struct {
|
type PKCS7 struct {
|
||||||
Content []byte
|
Content []byte
|
||||||
Certificates []*x509.Certificate
|
Certificates []*x509.Certificate
|
||||||
CRLs []pkix.CertificateList
|
CRLs []x509.RevocationList
|
||||||
Signers []signerInfo
|
Signers []signerInfo
|
||||||
raw interface{}
|
raw interface{}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ type signedData struct {
|
||||||
DigestAlgorithmIdentifiers []pkix.AlgorithmIdentifier `asn1:"set"`
|
DigestAlgorithmIdentifiers []pkix.AlgorithmIdentifier `asn1:"set"`
|
||||||
ContentInfo contentInfo
|
ContentInfo contentInfo
|
||||||
Certificates rawCertificates `asn1:"optional,tag:0"`
|
Certificates rawCertificates `asn1:"optional,tag:0"`
|
||||||
CRLs []pkix.CertificateList `asn1:"optional,tag:1"`
|
CRLs []x509.RevocationList `asn1:"optional,tag:1"`
|
||||||
SignerInfos []signerInfo `asn1:"set"`
|
SignerInfos []signerInfo `asn1:"set"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +110,9 @@ func marshalAttributes(attrs []attribute) ([]byte, error) {
|
||||||
|
|
||||||
// Remove the leading sequence octets
|
// Remove the leading sequence octets
|
||||||
var raw asn1.RawValue
|
var raw asn1.RawValue
|
||||||
asn1.Unmarshal(encodedAttributes, &raw)
|
if _, err := asn1.Unmarshal(encodedAttributes, &raw); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return raw.Bytes, nil
|
return raw.Bytes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,7 +379,7 @@ func marshalCertificates(certs []*x509.Certificate) rawCertificates {
|
||||||
// RawContent, we have to encode it into the RawContent. If its missing,
|
// RawContent, we have to encode it into the RawContent. If its missing,
|
||||||
// then `asn1.Marshal()` will strip out the certificate wrapper instead.
|
// then `asn1.Marshal()` will strip out the certificate wrapper instead.
|
||||||
func marshalCertificateBytes(certs []byte) (rawCertificates, error) {
|
func marshalCertificateBytes(certs []byte) (rawCertificates, error) {
|
||||||
var val = asn1.RawValue{Bytes: certs, Class: 2, Tag: 0, IsCompound: true}
|
val := asn1.RawValue{Bytes: certs, Class: 2, Tag: 0, IsCompound: true}
|
||||||
b, err := asn1.Marshal(val)
|
b, err := asn1.Marshal(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rawCertificates{}, err
|
return rawCertificates{}, err
|
||||||
|
|
|
@ -12,7 +12,6 @@ import (
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
@ -75,33 +74,53 @@ func TestOpenSSLVerifyDetachedSignature(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// write the root cert to a temp file
|
// write the root cert to a temp file
|
||||||
tmpRootCertFile, err := ioutil.TempFile("", "pkcs7TestRootCA")
|
tmpRootCertFile, err := os.CreateTemp("", "pkcs7TestRootCA")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer os.Remove(tmpRootCertFile.Name()) // clean up
|
defer func(name string) {
|
||||||
fd, err := os.OpenFile(tmpRootCertFile.Name(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
|
if err := os.Remove(name); err != nil {
|
||||||
|
t.Fatalf("Cannot write root cert: %s", err)
|
||||||
|
}
|
||||||
|
}(tmpRootCertFile.Name()) // clean up
|
||||||
|
fd, err := os.OpenFile(tmpRootCertFile.Name(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
pem.Encode(fd, &pem.Block{Type: "CERTIFICATE", Bytes: rootCert.Certificate.Raw})
|
if err := pem.Encode(fd, &pem.Block{Type: "CERTIFICATE", Bytes: rootCert.Certificate.Raw}); err != nil {
|
||||||
fd.Close()
|
t.Fatalf("Cannot write root cert: %s", err)
|
||||||
|
}
|
||||||
|
if err := fd.Close(); err != nil {
|
||||||
|
t.Fatalf("Cannot write root cert: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
// write the signature to a temp file
|
// write the signature to a temp file
|
||||||
tmpSignatureFile, err := ioutil.TempFile("", "pkcs7Signature")
|
tmpSignatureFile, err := os.CreateTemp("", "pkcs7Signature")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer os.Remove(tmpSignatureFile.Name()) // clean up
|
defer func(name string) {
|
||||||
ioutil.WriteFile(tmpSignatureFile.Name(), signed, 0755)
|
if err := os.Remove(name); err != nil {
|
||||||
|
t.Fatalf("Cannot write signature: %s", err)
|
||||||
|
}
|
||||||
|
}(tmpSignatureFile.Name()) // clean up
|
||||||
|
if err := os.WriteFile(tmpSignatureFile.Name(), signed, 0o755); err != nil {
|
||||||
|
t.Fatalf("Cannot write signature: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
// write the content to a temp file
|
// write the content to a temp file
|
||||||
tmpContentFile, err := ioutil.TempFile("", "pkcs7Content")
|
tmpContentFile, err := os.CreateTemp("", "pkcs7Content")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer os.Remove(tmpContentFile.Name()) // clean up
|
defer func(name string) {
|
||||||
ioutil.WriteFile(tmpContentFile.Name(), content, 0755)
|
if err := os.Remove(name); err != nil {
|
||||||
|
t.Fatalf("Cannot write content: %s", err)
|
||||||
|
}
|
||||||
|
}(tmpContentFile.Name()) // clean up
|
||||||
|
if err := os.WriteFile(tmpContentFile.Name(), content, 0o755); err != nil {
|
||||||
|
t.Fatalf("Cannot write content: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
// call openssl to verify the signature on the content using the root
|
// call openssl to verify the signature on the content using the root
|
||||||
opensslCMD := exec.Command("openssl", "smime", "-verify",
|
opensslCMD := exec.Command("openssl", "smime", "-verify",
|
||||||
|
|
86
smime.go
86
smime.go
|
@ -6,14 +6,14 @@ package mail
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"go.mozilla.org/pkcs7"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -22,35 +22,34 @@ var (
|
||||||
|
|
||||||
// ErrInvalidCertificate should be used if the certificate is invalid
|
// ErrInvalidCertificate should be used if the certificate is invalid
|
||||||
ErrInvalidCertificate = errors.New("invalid certificate")
|
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")
|
|
||||||
|
|
||||||
// ErrCouldNotAddSigner should be used if the signer could not be added
|
|
||||||
ErrCouldNotAddSigner = errors.New("could not add signer message")
|
|
||||||
|
|
||||||
// ErrCouldNotFinishSigning should be used if the signing could not be finished
|
|
||||||
ErrCouldNotFinishSigning = errors.New("could not finish signing")
|
|
||||||
|
|
||||||
// ErrCouldNoEncodeToPEM should be used if the signature could not be encoded to PEM
|
|
||||||
ErrCouldNoEncodeToPEM = errors.New("could not encode to PEM")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// privateKeyHolder is the representation of a private key
|
||||||
|
type privateKeyHolder struct {
|
||||||
|
ecdsa *ecdsa.PrivateKey
|
||||||
|
rsa *rsa.PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// get returns the private key of the privateKeyHolder
|
||||||
|
func (p privateKeyHolder) get() crypto.PrivateKey {
|
||||||
|
if p.ecdsa != nil {
|
||||||
|
return p.ecdsa
|
||||||
|
}
|
||||||
|
return p.rsa
|
||||||
|
}
|
||||||
|
|
||||||
// SMime is used to sign messages with S/MIME
|
// SMime is used to sign messages with S/MIME
|
||||||
type SMime struct {
|
type SMime struct {
|
||||||
privateKey *rsa.PrivateKey
|
privateKey privateKeyHolder
|
||||||
certificate *x509.Certificate
|
certificate *x509.Certificate
|
||||||
intermediateCertificate *x509.Certificate
|
intermediateCertificate *x509.Certificate
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSMime construct a new instance of SMime with provided parameters
|
// newSMimeWithRSA construct a new instance of SMime with provided parameters
|
||||||
// privateKey as *rsa.PrivateKey
|
// privateKey as *rsa.PrivateKey
|
||||||
// certificate as *x509.Certificate
|
// certificate as *x509.Certificate
|
||||||
// intermediateCertificate as *x509.Certificate
|
// intermediateCertificate (optional) as *x509.Certificate
|
||||||
func newSMime(privateKey *rsa.PrivateKey, certificate *x509.Certificate, intermediateCertificate *x509.Certificate) (*SMime, error) {
|
func newSMimeWithRSA(privateKey *rsa.PrivateKey, certificate *x509.Certificate, intermediateCertificate *x509.Certificate) (*SMime, error) {
|
||||||
if privateKey == nil {
|
if privateKey == nil {
|
||||||
return nil, ErrInvalidPrivateKey
|
return nil, ErrInvalidPrivateKey
|
||||||
}
|
}
|
||||||
|
@ -59,12 +58,28 @@ func newSMime(privateKey *rsa.PrivateKey, certificate *x509.Certificate, interme
|
||||||
return nil, ErrInvalidCertificate
|
return nil, ErrInvalidCertificate
|
||||||
}
|
}
|
||||||
|
|
||||||
if intermediateCertificate == nil {
|
return &SMime{
|
||||||
return nil, ErrInvalidIntermediateCertificate
|
privateKey: privateKeyHolder{rsa: privateKey},
|
||||||
|
certificate: certificate,
|
||||||
|
intermediateCertificate: intermediateCertificate,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// newSMimeWithECDSA construct a new instance of SMime with provided parameters
|
||||||
|
// privateKey as *ecdsa.PrivateKey
|
||||||
|
// certificate as *x509.Certificate
|
||||||
|
// intermediateCertificate (optional) as *x509.Certificate
|
||||||
|
func newSMimeWithECDSA(privateKey *ecdsa.PrivateKey, certificate *x509.Certificate, intermediateCertificate *x509.Certificate) (*SMime, error) {
|
||||||
|
if privateKey == nil {
|
||||||
|
return nil, ErrInvalidPrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
if certificate == nil {
|
||||||
|
return nil, ErrInvalidCertificate
|
||||||
}
|
}
|
||||||
|
|
||||||
return &SMime{
|
return &SMime{
|
||||||
privateKey: privateKey,
|
privateKey: privateKeyHolder{ecdsa: privateKey},
|
||||||
certificate: certificate,
|
certificate: certificate,
|
||||||
intermediateCertificate: intermediateCertificate,
|
intermediateCertificate: intermediateCertificate,
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -75,26 +90,29 @@ func (sm *SMime) signMessage(message string) (*string, error) {
|
||||||
lines := parseLines([]byte(message))
|
lines := parseLines([]byte(message))
|
||||||
toBeSigned := lines.bytesFromLines([]byte("\r\n"))
|
toBeSigned := lines.bytesFromLines([]byte("\r\n"))
|
||||||
|
|
||||||
signedData, err := pkcs7.NewSignedData(toBeSigned)
|
signedData, err := newSignedData(toBeSigned)
|
||||||
signedData.SetDigestAlgorithm(pkcs7.OIDDigestAlgorithmSHA256)
|
if err != nil || signedData == nil {
|
||||||
if err != nil {
|
return nil, fmt.Errorf("could not initialize signed data: %w", err)
|
||||||
return nil, ErrCouldNotInitialize
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = signedData.AddSignerChain(sm.certificate, sm.privateKey, []*x509.Certificate{sm.intermediateCertificate}, pkcs7.SignerInfoConfig{}); err != nil {
|
if err = signedData.addSigner(sm.certificate, sm.privateKey.get(), SignerInfoConfig{}); err != nil {
|
||||||
return nil, ErrCouldNotAddSigner
|
return nil, fmt.Errorf("could not add signer message: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
signedData.Detach()
|
if sm.intermediateCertificate != nil {
|
||||||
|
signedData.addCertificate(sm.intermediateCertificate)
|
||||||
|
}
|
||||||
|
|
||||||
signatureDER, err := signedData.Finish()
|
signedData.detach()
|
||||||
|
|
||||||
|
signatureDER, err := signedData.finish()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ErrCouldNotFinishSigning
|
return nil, fmt.Errorf("could not finish signing: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pemMsg, err := encodeToPEM(signatureDER)
|
pemMsg, err := encodeToPEM(signatureDER)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ErrCouldNoEncodeToPEM
|
return nil, fmt.Errorf("could not encode to PEM: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return pemMsg, nil
|
return pemMsg, nil
|
||||||
|
|
|
@ -6,25 +6,72 @@ package mail
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/rsa"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestNewSMime tests the newSMime method
|
func TestGet_RSA(t *testing.T) {
|
||||||
func TestNewSMime(t *testing.T) {
|
p := privateKeyHolder{
|
||||||
privateKey, certificate, intermediateCertificate, err := getDummyCryptoMaterial()
|
ecdsa: nil,
|
||||||
|
rsa: &rsa.PrivateKey{},
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.get() == nil {
|
||||||
|
t.Errorf("get() did not return the correct private key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGet_ECDSA(t *testing.T) {
|
||||||
|
p := privateKeyHolder{
|
||||||
|
ecdsa: &ecdsa.PrivateKey{},
|
||||||
|
rsa: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.get() == nil {
|
||||||
|
t.Errorf("get() did not return the correct private key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestNewSMimeWithRSA tests the newSMime method with RSA crypto material
|
||||||
|
func TestNewSMimeWithRSA(t *testing.T) {
|
||||||
|
privateKey, certificate, intermediateCertificate, err := getDummyRSACryptoMaterial()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error getting dummy crypto material: %s", err)
|
t.Errorf("Error getting dummy crypto material: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sMime, err := newSMime(privateKey, certificate, intermediateCertificate)
|
sMime, err := newSMimeWithRSA(privateKey, certificate, intermediateCertificate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error creating new SMime from keyPair: %s", err)
|
t.Errorf("Error creating new SMime from keyPair: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if sMime.privateKey != privateKey {
|
if sMime.privateKey.rsa != privateKey {
|
||||||
|
t.Errorf("NewSMime() did not return the same private key")
|
||||||
|
}
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestNewSMimeWithECDSA tests the newSMime method with ECDSA crypto material
|
||||||
|
func TestNewSMimeWithECDSA(t *testing.T) {
|
||||||
|
privateKey, certificate, intermediateCertificate, err := getDummyECDSACryptoMaterial()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error getting dummy crypto material: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sMime, err := newSMimeWithECDSA(privateKey, certificate, intermediateCertificate)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error creating new SMime from keyPair: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if sMime.privateKey.ecdsa != privateKey {
|
||||||
t.Errorf("NewSMime() did not return the same private key")
|
t.Errorf("NewSMime() did not return the same private key")
|
||||||
}
|
}
|
||||||
if sMime.certificate != certificate {
|
if sMime.certificate != certificate {
|
||||||
|
@ -37,12 +84,12 @@ func TestNewSMime(t *testing.T) {
|
||||||
|
|
||||||
// TestSign tests the sign method
|
// TestSign tests the sign method
|
||||||
func TestSign(t *testing.T) {
|
func TestSign(t *testing.T) {
|
||||||
privateKey, certificate, intermediateCertificate, err := getDummyCryptoMaterial()
|
privateKey, certificate, intermediateCertificate, err := getDummyRSACryptoMaterial()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error getting dummy crypto material: %s", err)
|
t.Errorf("Error getting dummy crypto material: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sMime, err := newSMime(privateKey, certificate, intermediateCertificate)
|
sMime, err := newSMimeWithRSA(privateKey, certificate, intermediateCertificate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error creating new SMime from keyPair: %s", err)
|
t.Errorf("Error creating new SMime from keyPair: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -60,12 +107,12 @@ func TestSign(t *testing.T) {
|
||||||
|
|
||||||
// TestPrepareMessage tests the createMessage method
|
// TestPrepareMessage tests the createMessage method
|
||||||
func TestPrepareMessage(t *testing.T) {
|
func TestPrepareMessage(t *testing.T) {
|
||||||
privateKey, certificate, intermediateCertificate, err := getDummyCryptoMaterial()
|
privateKey, certificate, intermediateCertificate, err := getDummyRSACryptoMaterial()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error getting dummy crypto material: %s", err)
|
t.Errorf("Error getting dummy crypto material: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sMime, err := newSMime(privateKey, certificate, intermediateCertificate)
|
sMime, err := newSMimeWithRSA(privateKey, certificate, intermediateCertificate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error creating new SMime from keyPair: %s", err)
|
t.Errorf("Error creating new SMime from keyPair: %s", err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue