5f34d82562
- fixed parsing of choice(was compatible with Apple mail.app which tagged kari explicitly) - minor fixes
253 lines
6.0 KiB
Go
253 lines
6.0 KiB
Go
// Package cms contains high level functions for cryptographic message syntax RFC 5652.
|
|
package cms
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"encoding/asn1"
|
|
"fmt"
|
|
"log"
|
|
"time"
|
|
|
|
protocol "github.com/InfiniteLoopSpace/go_S-MIME/cms/protocol"
|
|
oid "github.com/InfiniteLoopSpace/go_S-MIME/oid"
|
|
timestamp "github.com/InfiniteLoopSpace/go_S-MIME/timestamp"
|
|
)
|
|
|
|
// CMS is an instance of cms to en-/decrypt and sign/verfiy CMS data
|
|
// with the given keyPairs and options.
|
|
type CMS struct {
|
|
Intermediate, roots *x509.CertPool
|
|
Opts x509.VerifyOptions
|
|
ContentEncryptionAlgorithm asn1.ObjectIdentifier
|
|
TimeStampServer string
|
|
TimeStamp bool
|
|
keyPairs []tls.Certificate
|
|
}
|
|
|
|
// New create a new instance of CMS with given keyPairs.
|
|
func New(cert ...tls.Certificate) (cms *CMS, err error) {
|
|
root, err := x509.SystemCertPool()
|
|
intermediate := x509.NewCertPool()
|
|
cms = &CMS{
|
|
Intermediate: intermediate,
|
|
roots: root,
|
|
Opts: x509.VerifyOptions{
|
|
Intermediates: intermediate,
|
|
Roots: root,
|
|
CurrentTime: time.Now(),
|
|
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
|
|
},
|
|
ContentEncryptionAlgorithm: oid.EncryptionAlgorithmAES128CBC,
|
|
TimeStampServer: "http://timestamp.digicert.com",
|
|
TimeStamp: false,
|
|
}
|
|
cms.keyPairs = cert
|
|
|
|
for i := range cms.keyPairs {
|
|
cms.keyPairs[i].Leaf, err = x509.ParseCertificate(cms.keyPairs[i].Certificate[0])
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
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) {
|
|
|
|
eci, key, _, err := protocol.NewEncryptedContentInfo(oid.Data, cms.ContentEncryptionAlgorithm, data)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
var reciInfos []protocol.RecipientInfo
|
|
|
|
for _, recipient := range recipients {
|
|
var rInfo protocol.RecipientInfo
|
|
rInfo, err = protocol.NewRecipientInfo(recipient, key)
|
|
if err != nil {
|
|
return
|
|
}
|
|
reciInfos = append(reciInfos, rInfo)
|
|
}
|
|
|
|
ed := protocol.NewEnvelopedData(&eci, reciInfos)
|
|
|
|
ci, err := ed.ContentInfo()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
return ci.DER()
|
|
}
|
|
|
|
// AuthEncrypt AEAD-encrypts data for the recipients and returns DER-encoded ASN.1 ContentInfo.
|
|
func (cms *CMS) AuthEncrypt(data []byte, recipients []*x509.Certificate) (der []byte, err error) {
|
|
|
|
eci, key, mac, err := protocol.NewEncryptedContentInfo(oid.Data, oid.EncryptionAlgorithmAES128GCM, data)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
var reciInfos []protocol.RecipientInfo
|
|
|
|
for _, recipient := range recipients {
|
|
var rInfo protocol.RecipientInfo
|
|
rInfo, err = protocol.NewRecipientInfo(recipient, key)
|
|
if err != nil {
|
|
return
|
|
}
|
|
reciInfos = append(reciInfos, rInfo)
|
|
}
|
|
|
|
ed := protocol.NewAuthEnvelopedData(&eci, reciInfos, mac)
|
|
|
|
ci, err := ed.ContentInfo()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
return ci.DER()
|
|
}
|
|
|
|
// AuthDecrypt AEAD-decrypts DER-encoded ASN.1 ContentInfo and returns plaintext.
|
|
func (cms *CMS) AuthDecrypt(contentInfo []byte) (plain []byte, err error) {
|
|
contInf, err := protocol.ParseContentInfo(contentInfo)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
ed, err := contInf.AuthEnvelopedDataContent()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
plain, err = ed.Decrypt(cms.keyPairs)
|
|
|
|
return
|
|
}
|
|
|
|
// Decrypt decrypts DER-encoded ASN.1 ContentInfo and returns plaintext.
|
|
func (cms *CMS) Decrypt(contentInfo []byte) (plain []byte, err error) {
|
|
contInf, err := protocol.ParseContentInfo(contentInfo)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
ed, err := contInf.EnvelopedDataContent()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
plain, err = ed.Decrypt(cms.keyPairs)
|
|
|
|
return
|
|
}
|
|
|
|
// Sign signs the data and returns returns DER-encoded ASN.1 ContentInfo.
|
|
func (cms *CMS) Sign(data []byte, detachedSignature ...bool) (der []byte, err error) {
|
|
|
|
enci, err := protocol.NewDataEncapsulatedContentInfo(data)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
|
|
sd, err := protocol.NewSignedData(enci)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
|
|
for i := range cms.keyPairs {
|
|
sd.AddSignerInfo(cms.keyPairs[i])
|
|
}
|
|
|
|
if cms.TimeStamp {
|
|
err1 := AddTimestamps(sd, cms.TimeStampServer)
|
|
if err1 != nil {
|
|
log.Println(err1)
|
|
}
|
|
}
|
|
|
|
if len(detachedSignature) > 0 && detachedSignature[0] {
|
|
sd.EncapContentInfo.EContent = nil
|
|
}
|
|
|
|
ci, err := sd.ContentInfo()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
return ci.DER()
|
|
}
|
|
|
|
// Verify verifies the signature in contentInfo and returns returns DER-encoded ASN.1 ContentInfo.
|
|
func (cms *CMS) Verify(contentInfo []byte) (chains [][][]*x509.Certificate, err error) {
|
|
ci, err := protocol.ParseContentInfo(contentInfo)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
sd, err := ci.SignedDataContent()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
chains, err = sd.Verify(cms.Opts, nil)
|
|
|
|
return
|
|
}
|
|
|
|
// VerifyDetached verifies the detached signature of msg in contentInfo and returns returns DER-encoded ASN.1 ContentInfo.
|
|
func (cms *CMS) VerifyDetached(contentInfo, msg []byte) (chains [][][]*x509.Certificate, err error) {
|
|
|
|
ci, err := protocol.ParseContentInfo(contentInfo)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
sd, err := ci.SignedDataContent()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
chains, err = sd.Verify(cms.Opts, msg)
|
|
|
|
return
|
|
}
|
|
|
|
// AddTimestamps adds a timestamp to the SignedData using the RFC3161
|
|
// timestamping service at the given URL. This timestamp proves that the signed
|
|
// message existed the time of generation, allowing verifiers to have more trust
|
|
// in old messages signed with revoked keys.
|
|
func AddTimestamps(sd *protocol.SignedData, url string) (err error) {
|
|
var attrs = make([]protocol.Attribute, len(sd.SignerInfos))
|
|
|
|
// Fetch all timestamp tokens before adding any to sd. This avoids a partial
|
|
// failure.
|
|
for i := range attrs {
|
|
hash, err := sd.SignerInfos[i].Hash()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
tsToken, err := timestamp.FetchTSToken(url, sd.SignerInfos[i].Signature, hash)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
attr, err := protocol.NewAttribute(oid.AttributeTimeStampToken, tsToken)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
attrs[i] = attr
|
|
}
|
|
|
|
for i := range attrs {
|
|
sd.SignerInfos[i].UnsignedAttrs = append(sd.SignerInfos[i].UnsignedAttrs, attrs[i])
|
|
}
|
|
|
|
return nil
|
|
}
|