1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
42
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
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
61 func (sb *SignatureBuilder) SetContentData(data []byte) error {
62 return sb.SetContent(OidData, data)
63 }
64
65
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
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
90 func (sb *SignatureBuilder) AddAuthenticatedAttribute(oid asn1.ObjectIdentifier, data interface{}) error {
91 return sb.authAttrs.Add(oid, data)
92 }
93
94
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
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
117
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