mirror of
https://github.com/wneessen/go-mail.git
synced 2024-11-22 13:50:49 +01:00
implementation of S/MIME singing without tests of type smime
This commit is contained in:
parent
e0a59dba6d
commit
94942ed383
5 changed files with 114 additions and 6 deletions
2
go.mod
2
go.mod
|
@ -5,3 +5,5 @@
|
||||||
module github.com/wneessen/go-mail
|
module github.com/wneessen/go-mail
|
||||||
|
|
||||||
go 1.16
|
go 1.16
|
||||||
|
|
||||||
|
require go.mozilla.org/pkcs7 v0.9.0
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -0,0 +1,2 @@
|
||||||
|
go.mozilla.org/pkcs7 v0.9.0 h1:yM4/HS9dYv7ri2biPtxt8ikvB37a980dg69/pKmS+eI=
|
||||||
|
go.mozilla.org/pkcs7 v0.9.0/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
|
41
msg.go
41
msg.go
|
@ -7,6 +7,8 @@ package mail
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
"embed"
|
"embed"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -981,10 +983,47 @@ func (m *Msg) applyMiddlewares(msg *Msg) *Msg {
|
||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// signMessage sign the Msg with S/MIME
|
||||||
|
func (m *Msg) signMessage(msg *Msg) (*Msg, error) {
|
||||||
|
currentPart := m.GetParts()[0]
|
||||||
|
currentPart.SetEncoding(EncodingUSASCII)
|
||||||
|
currentPart.SetContentType(TypeTextPlain)
|
||||||
|
content, err := currentPart.GetContent()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("failed to extract content from part")
|
||||||
|
}
|
||||||
|
|
||||||
|
signedContent, err := m.sMime.Sign(content)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("failed to sign message")
|
||||||
|
}
|
||||||
|
|
||||||
|
signedPart := msg.newPart(
|
||||||
|
typeSMimeSigned,
|
||||||
|
WithPartEncoding(EncodingB64),
|
||||||
|
WithContentDisposition(DispositionSMime),
|
||||||
|
)
|
||||||
|
signedPart.SetContent(*signedContent)
|
||||||
|
msg.parts = append(msg.parts, signedPart)
|
||||||
|
|
||||||
|
return msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
// WriteTo writes the formated Msg into a give io.Writer and satisfies the io.WriteTo interface
|
// WriteTo writes the formated Msg into a give io.Writer and satisfies the io.WriteTo interface
|
||||||
func (m *Msg) WriteTo(writer io.Writer) (int64, error) {
|
func (m *Msg) WriteTo(writer io.Writer) (int64, error) {
|
||||||
mw := &msgWriter{writer: writer, charset: m.charset, encoder: m.encoder}
|
mw := &msgWriter{writer: writer, charset: m.charset, encoder: m.encoder}
|
||||||
mw.writeMsg(m.applyMiddlewares(m))
|
msg := m.applyMiddlewares(m)
|
||||||
|
|
||||||
|
if m.sMime != nil {
|
||||||
|
signedMsg, err := m.signMessage(msg)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
msg = signedMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
mw.writeMsg(msg)
|
||||||
|
|
||||||
return mw.bytesWritten, mw.err
|
return mw.bytesWritten, mw.err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,6 +88,10 @@ func (mw *msgWriter) writeMsg(msg *Msg) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if msg.hasSMime() {
|
||||||
|
mw.startMP(MIMESMime, msg.boundary)
|
||||||
|
mw.writeString(DoubleNewLine)
|
||||||
|
}
|
||||||
if msg.hasMixed() {
|
if msg.hasMixed() {
|
||||||
mw.startMP(MIMEMixed, msg.boundary)
|
mw.startMP(MIMEMixed, msg.boundary)
|
||||||
mw.writeString(DoubleNewLine)
|
mw.writeString(DoubleNewLine)
|
||||||
|
@ -96,7 +100,7 @@ func (mw *msgWriter) writeMsg(msg *Msg) {
|
||||||
mw.startMP(MIMERelated, msg.boundary)
|
mw.startMP(MIMERelated, msg.boundary)
|
||||||
mw.writeString(DoubleNewLine)
|
mw.writeString(DoubleNewLine)
|
||||||
}
|
}
|
||||||
if msg.hasAlt() {
|
if msg.hasAlt() && !msg.hasSMime() {
|
||||||
mw.startMP(MIMEAlternative, msg.boundary)
|
mw.startMP(MIMEAlternative, msg.boundary)
|
||||||
mw.writeString(DoubleNewLine)
|
mw.writeString(DoubleNewLine)
|
||||||
}
|
}
|
||||||
|
@ -265,6 +269,9 @@ func (mw *msgWriter) writePart(part *Part, charset Charset) {
|
||||||
if part.description != "" {
|
if part.description != "" {
|
||||||
mimeHeader.Add(string(HeaderContentDescription), part.description)
|
mimeHeader.Add(string(HeaderContentDescription), part.description)
|
||||||
}
|
}
|
||||||
|
if part.disposition != "" {
|
||||||
|
mimeHeader.Add(string(HeaderContentDisposition), part.disposition.String())
|
||||||
|
}
|
||||||
mimeHeader.Add(string(HeaderContentType), contentType)
|
mimeHeader.Add(string(HeaderContentType), contentType)
|
||||||
mimeHeader.Add(string(HeaderContentTransferEnc), contentTransferEnc)
|
mimeHeader.Add(string(HeaderContentTransferEnc), contentTransferEnc)
|
||||||
mw.newPart(mimeHeader)
|
mw.newPart(mimeHeader)
|
||||||
|
|
66
sime.go
66
sime.go
|
@ -3,10 +3,68 @@ package mail
|
||||||
import (
|
import (
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"errors"
|
||||||
|
"go.mozilla.org/pkcs7"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SMimeAuthConfig represents the authentication type for s/mime crypto key material
|
var (
|
||||||
type SMimeAuthConfig struct {
|
// ErrInvalidPrivateKey should be used if private key is invalid
|
||||||
Certificate *x509.Certificate
|
ErrInvalidPrivateKey = errors.New("invalid private key")
|
||||||
PrivateKey *rsa.PrivateKey
|
|
||||||
|
// ErrInvalidCertificate should be used if certificate is invalid
|
||||||
|
ErrInvalidCertificate = errors.New("invalid 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")
|
||||||
|
)
|
||||||
|
|
||||||
|
// SMime is used to sign messages with S/MIME
|
||||||
|
type SMime struct {
|
||||||
|
privateKey *rsa.PrivateKey
|
||||||
|
certificate *x509.Certificate
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSMime construct a new instance of SMime with a provided *rsa.PrivateKey
|
||||||
|
func NewSMime(privateKey *rsa.PrivateKey, certificate *x509.Certificate) (*SMime, error) {
|
||||||
|
if privateKey == nil {
|
||||||
|
return nil, ErrInvalidPrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
if certificate == nil {
|
||||||
|
return nil, ErrInvalidCertificate
|
||||||
|
}
|
||||||
|
|
||||||
|
return &SMime{
|
||||||
|
privateKey: privateKey,
|
||||||
|
certificate: certificate,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign the content with the given privateKey of the method NewSMime
|
||||||
|
func (sm *SMime) Sign(content []byte) (*string, error) {
|
||||||
|
toBeSigned, err := pkcs7.NewSignedData(content)
|
||||||
|
|
||||||
|
toBeSigned.SetDigestAlgorithm(pkcs7.OIDDigestAlgorithmSHA256)
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrCouldNotInitialize
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = toBeSigned.AddSigner(sm.certificate, sm.privateKey, pkcs7.SignerInfoConfig{}); err != nil {
|
||||||
|
return nil, ErrCouldNotAddSigner
|
||||||
|
}
|
||||||
|
|
||||||
|
signed, err := toBeSigned.Finish()
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrCouldNotFinishSigning
|
||||||
|
}
|
||||||
|
|
||||||
|
signedData := string(signed)
|
||||||
|
|
||||||
|
return &signedData, nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue