Add support for signing E-Mails and fixed tests.

This commit is contained in:
InfiniteLoopSpace
2018-11-19 14:33:55 +01:00
parent 63fb32eb40
commit 830a60b19f
7 changed files with 328 additions and 62 deletions

View File

@ -8,6 +8,7 @@ import (
"crypto/x509"
"encoding/base64"
"errors"
"log"
"strings"
"github.com/InfiniteLoopSpace/go_S-MIME/b64"
@ -42,8 +43,11 @@ func (smime *SMIME) Decrypt(msg []byte) (plaintext []byte, err error) {
mediaType, params, err := mail.ParseMediaType()
if !strings.HasPrefix(mediaType, "application/pkcs7-mime") {
err = errors.New("Unsupported media type: Can not decrypt this mail")
return
if !strings.HasPrefix(mediaType, "application/x-pkcs7-mime") {
err = errors.New("Unsupported media type: Can not decrypt this mail")
return
}
log.Println("Found Content-Type \"application/x-pkcs7-mime\" used early implementations of S/MIME agents")
}
if !strings.HasPrefix(params["smime-type"], "enveloped-data") {
@ -147,13 +151,16 @@ func (smime *SMIME) Verify(msg []byte) (chains [][][]*x509.Certificate, err erro
mediaType, params, err := mail.ParseMediaType()
if !strings.HasPrefix(mediaType, "multipart/signed") {
err = errors.New("Unsupported media type: can not decrypt this mail")
err = errors.New("Unsupported media type: can not verify the signature")
return
}
if !strings.HasPrefix(params["protocol"], "application/pkcs7-signature") {
err = errors.New("Unsupported smime type: can not decrypt this mail")
return
if !strings.HasPrefix(params["protocol"], "application/x-pkcs7-signature") {
err = errors.New("Unsupported smime type: can not verify the signature")
return
}
log.Println("Found Content-Type \"application/x-pkcs7-signature\" used early implementations of S/MIME agents")
}
parts, err := mail.MultipartGetParts()
@ -170,8 +177,11 @@ func (smime *SMIME) Verify(msg []byte) (chains [][][]*x509.Certificate, err erro
mediaType, params, err = signature.ParseMediaType()
if !strings.HasPrefix(mediaType, "application/pkcs7-signature") {
err = errors.New("Unsupported media type: Can not decrypt this mail")
return
if !strings.HasPrefix(mediaType, "application/x-pkcs7-signature") {
err = errors.New("Unsupported media type: Can not decrypt this mail")
return
}
log.Println("Found Content-Type \"application/x-pkcs7-signature\" used early implementations of S/MIME agents")
}
contentTransferEncoding := signature.GetHeaderField([]byte("Content-Transfer-Encoding"))
@ -198,3 +208,52 @@ func (smime *SMIME) Verify(msg []byte) (chains [][][]*x509.Certificate, err erro
return smime.CMS.VerifyDetached(signatureDer, signedMsg)
}
// Sign signs a mail and returns the signed message.
func (smime *SMIME) Sign(msg []byte) (signedMsg []byte, err error) {
mail := mime.Parse(msg)
// Prepare the signed Part
signedPart := mime.MIME{}
signedPart.SetBody(mail.Body())
contentType := mail.GetHeaderField([]byte("Content-Type"))
if len(contentType) != 1 {
err = errors.New("Message has no Content-Type")
return
}
signedPart.SetHeaderField([]byte("Content-Type"), contentType[0])
contentTransferEncoding := mail.GetHeaderField([]byte("Content-Transfer-Encoding"))
if len(contentType) == 1 {
signedPart.SetHeaderField([]byte("Content-Transfer-Encoding"), contentTransferEncoding[0])
}
contentDisposition := mail.GetHeaderField([]byte("Content-Disposition"))
if len(contentType) == 1 {
signedPart.SetHeaderField([]byte("Content-Disposition"), contentDisposition[0])
}
// Sign
lines := mime.ParseLines(signedPart.Full())
signatureDER, err := smime.CMS.Sign(lines.Bytes(mime.CRLF), true)
// Encode signature
signature := mime.MIME{}
signature.SetHeaderField([]byte("Content-Type"), []byte("application/pkcs7-signature; name=smime.p7s"))
signature.SetHeaderField([]byte("Content-Transfer-Encoding"), []byte("base64"))
signature.SetHeaderField([]byte("Content-Disposition"), []byte("attachment; filename=smime.p7s"))
signatureBASE64, err := b64.EncodeBase64(signatureDER)
if err != nil {
return
}
signature.SetBody(signatureBASE64)
// Make multipart/signed message
micAlg := "sha256"
cntType := "multipart/signed;\n protocol=\"application/pkcs7-signature\";\n micalg=" + micAlg
mail.SetMultipartBody(cntType, signedPart, signature)
signedMsg = mail.Full()
return
}

View File

@ -2,10 +2,166 @@ package smime
import (
"bytes"
"crypto"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"log"
"testing"
"github.com/InfiniteLoopSpace/go_S-MIME/openssl"
"github.com/InfiniteLoopSpace/go_S-MIME/pki"
)
var (
root = pki.New(pki.IsCA, pki.Subject(pkix.Name{
CommonName: "root.example.com",
}))
intermediate = root.Issue(pki.IsCA, pki.Subject(pkix.Name{
CommonName: "intermediate.example.com",
}))
leaf = intermediate.Issue(pki.Subject(pkix.Name{
CommonName: "leaf.example.com",
}))
keyPair = tls.Certificate{
Certificate: [][]byte{leaf.Certificate.Raw, intermediate.Certificate.Raw, root.Certificate.Raw},
PrivateKey: leaf.PrivateKey.(crypto.PrivateKey),
}
)
func TestEnryptDecrypt(t *testing.T) {
SMIME, err := New(keyPair)
if err != nil {
t.Error(err)
}
plaintext := []byte(msg)
ciphertext, err := SMIME.Encrypt(plaintext, []*x509.Certificate{leaf.Certificate})
if err != nil {
t.Error(err)
}
fmt.Printf("%s\n", ciphertext)
plain, err := SMIME.Decrypt(ciphertext)
if err != nil {
log.Fatal(err)
}
if !bytes.Equal(plaintext, plain) {
t.Fatal("Encryption and decryption are not inverse")
}
}
func TestSignVerify(t *testing.T) {
SMIME, err := New(keyPair)
if err != nil {
t.Error(err)
}
SMIME.CMS.Opts.Roots.AddCert(root.Certificate)
msg := []byte(msg)
der, err := SMIME.Sign(msg)
if err != nil {
t.Error(err)
}
_, err = SMIME.Verify(der)
if err != nil {
t.Error(err)
}
}
func TestEncryptOpenSSL(t *testing.T) {
message := []byte("Hallo Welt!")
der, err := openssl.Encrypt(message, leaf.Certificate)
if err != nil {
t.Error(err)
}
SMIME, err := New(keyPair)
plain, err := SMIME.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) {
message := []byte(msg)
SMIME, _ := New()
ciphertext, err := SMIME.Encrypt(message, []*x509.Certificate{leaf.Certificate})
if err != nil {
t.Error(err)
}
plain, err := openssl.Decrypt(ciphertext, leaf.PrivateKey)
if err != nil {
t.Error(err)
}
if !bytes.Equal(message, plain) {
t.Fatal("Encryption and decryption are not inverse")
}
}
func TestSignOpenSSL(t *testing.T) {
message := []byte(msg)
sig, err := openssl.Sign(message, leaf.Certificate, leaf.PrivateKey, []*x509.Certificate{intermediate.Certificate})
if err != nil {
t.Error(err)
}
SMIME, err := New()
if err != nil {
t.Error(err)
}
SMIME.CMS.Opts.Roots.AddCert(root.Certificate)
_, err = SMIME.Verify(sig)
if err != nil {
t.Error(err)
}
}
func TestVerifyOpenSSL(t *testing.T) {
SMIME, err := New(keyPair)
if err != nil {
t.Error(err)
}
SMIME.CMS.Opts.Roots.AddCert(root.Certificate)
msg := []byte(msg)
der, err := SMIME.Sign(msg)
if err != nil {
t.Error(err)
}
sig, err := openssl.Verify(der, root.Certificate)
if err != nil {
t.Error(err)
}
if !bytes.Contains(msg, bytes.Replace(sig, []byte("\r"), nil, -1)) {
t.Fatal("Signed message and message do not agree!")
}
}
func TestDecrypt(t *testing.T) {
cert, err := tls.X509KeyPair([]byte(bobCert), []byte(bobRSAkey))
if err != nil {