...

Source file src/github.com/sassoftware/relic/lib/pkcs7/builder.go

Documentation: github.com/sassoftware/relic/lib/pkcs7

     1  //
     2  // Copyright (c) SAS Institute Inc.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  //
    16  
    17  package pkcs7
    18  
    19  import (
    20  	"crypto"
    21  	"crypto/rand"
    22  	"crypto/x509"
    23  	"crypto/x509/pkix"
    24  	"encoding/asn1"
    25  	"errors"
    26  	"fmt"
    27  
    28  	"github.com/sassoftware/relic/lib/x509tools"
    29  )
    30  
    31  type SignatureBuilder struct {
    32  	contentInfo ContentInfo
    33  	hash        crypto.Hash
    34  	digest      []byte
    35  	certs       []*x509.Certificate
    36  	privateKey  crypto.Signer
    37  	signerOpts  crypto.SignerOpts
    38  	authAttrs   AttributeList
    39  }
    40  
    41  // Build a PKCS#7 signature procedurally. Returns a structure that can have
    42  // content and attributes attached to it.
    43  func NewBuilder(privKey crypto.Signer, certs []*x509.Certificate, opts crypto.SignerOpts) *SignatureBuilder {
    44  	return &SignatureBuilder{
    45  		privateKey: privKey,
    46  		signerOpts: opts,
    47  		certs:      certs,
    48  	}
    49  }
    50  
    51  // Embed bytes or a structure into the PKCS#7 content
    52  func (sb *SignatureBuilder) SetContent(ctype asn1.ObjectIdentifier, data interface{}) error {
    53  	cinfo, err := NewContentInfo(ctype, data)
    54  	if err != nil {
    55  		return err
    56  	}
    57  	return sb.SetContentInfo(cinfo)
    58  }
    59  
    60  // Set content to a generic "data" blob
    61  func (sb *SignatureBuilder) SetContentData(data []byte) error {
    62  	return sb.SetContent(OidData, data)
    63  }
    64  
    65  // Set a ContentInfo structure as the PKCS#7 content
    66  func (sb *SignatureBuilder) SetContentInfo(cinfo ContentInfo) error {
    67  	blob, err := cinfo.Bytes()
    68  	if err != nil {
    69  		return err
    70  	}
    71  	d := sb.signerOpts.HashFunc().New()
    72  	d.Write(blob)
    73  	sb.contentInfo = cinfo
    74  	sb.digest = d.Sum(nil)
    75  	return nil
    76  }
    77  
    78  // Set a "detached" content type, with digest
    79  func (sb *SignatureBuilder) SetDetachedContent(ctype asn1.ObjectIdentifier, digest []byte) error {
    80  	if len(digest) != sb.signerOpts.HashFunc().Size() {
    81  		return errors.New("digest size mismatch")
    82  	}
    83  	cinfo, _ := NewContentInfo(ctype, nil)
    84  	sb.contentInfo = cinfo
    85  	sb.digest = digest
    86  	return nil
    87  }
    88  
    89  // Add an authenticated attribute to SignerInfo
    90  func (sb *SignatureBuilder) AddAuthenticatedAttribute(oid asn1.ObjectIdentifier, data interface{}) error {
    91  	return sb.authAttrs.Add(oid, data)
    92  }
    93  
    94  // Complete the signature and return the full PKCS#7 structure
    95  func (sb *SignatureBuilder) Sign() (*ContentInfoSignedData, error) {
    96  	if sb.digest == nil {
    97  		return nil, errors.New("SetContent was not called")
    98  	}
    99  	pubKey := sb.privateKey.Public()
   100  	digestAlg, pkeyAlg, err := x509tools.PkixAlgorithms(pubKey, sb.signerOpts)
   101  	if err != nil {
   102  		return nil, fmt.Errorf("pkcs7: %s", err)
   103  	}
   104  	if len(sb.certs) < 1 || !x509tools.SameKey(pubKey, sb.certs[0].PublicKey) {
   105  		return nil, errors.New("pkcs7: first certificate must match private key")
   106  	}
   107  	digest := sb.digest
   108  	if sb.authAttrs != nil {
   109  		// When authenticated attributes are present, then these are required.
   110  		if err := sb.authAttrs.Add(OidAttributeContentType, sb.contentInfo.ContentType); err != nil {
   111  			return nil, err
   112  		}
   113  		if err := sb.authAttrs.Add(OidAttributeMessageDigest, sb.digest); err != nil {
   114  			return nil, err
   115  		}
   116  		// Now the signature is over the authenticated attributes instead of
   117  		// the content directly.
   118  		attrbytes, err := sb.authAttrs.Bytes()
   119  		if err != nil {
   120  			return nil, err
   121  		}
   122  		w := sb.signerOpts.HashFunc().New()
   123  		w.Write(attrbytes)
   124  		digest = w.Sum(nil)
   125  	}
   126  	sig, err := sb.privateKey.Sign(rand.Reader, digest, sb.signerOpts)
   127  	if err != nil {
   128  		return nil, err
   129  	}
   130  	return &ContentInfoSignedData{
   131  		ContentType: OidSignedData,
   132  		Content: SignedData{
   133  			Version:                    1,
   134  			DigestAlgorithmIdentifiers: []pkix.AlgorithmIdentifier{digestAlg},
   135  			ContentInfo:                sb.contentInfo,
   136  			Certificates:               marshalCertificates(sb.certs),
   137  			CRLs:                       nil,
   138  			SignerInfos: []SignerInfo{SignerInfo{
   139  				Version: 1,
   140  				IssuerAndSerialNumber: IssuerAndSerial{
   141  					IssuerName:   asn1.RawValue{FullBytes: sb.certs[0].RawIssuer},
   142  					SerialNumber: sb.certs[0].SerialNumber,
   143  				},
   144  				DigestAlgorithm:           digestAlg,
   145  				DigestEncryptionAlgorithm: pkeyAlg,
   146  				AuthenticatedAttributes:   sb.authAttrs,
   147  				EncryptedDigest:           sig,
   148  			}},
   149  		},
   150  	}, nil
   151  }
   152  

View as plain text