-
Notifications
You must be signed in to change notification settings - Fork 1
/
pkcs7.go
290 lines (260 loc) · 9.67 KB
/
pkcs7.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
// {{{ Copyright (c) Doowon Kim <gotapenny@gmail.com>, 2019
// Parsing a PKCS7 SignedData in the code signing PKI (authenticode)
// This project focuses on only the code signing PKI so that only signedData is handled.
// This project is inspired by referenced from two projects 1) https://github.com/fullsailor/pkcs7 and 2) https://github.com/paultag/go-pkcs7
// Currently it supports only extracting information from pcks7 signeddata.
package mypkcs7
import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"errors"
"math/big"
"time"
)
var (
//OIDSignedData is signedData OIDs
OIDSignedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 2}
//OIDContentType is one of Attributes
OIDContentType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 3}
//OIDSigningTime is one of Attributes
OIDSigningTime = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 5}
//OIDMessageDigest is one of Attributes
OIDMessageDigest = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 4}
//OIDPKCS7 is PKCS#7
OIDPKCS7 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1}
//OIDSpcStatementTypeObjID 1.3.6.1.4.1.311.2.1.11
OIDSpcStatementTypeObjID = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 2, 1, 11}
//OIDSpcSPOpusInfoObjID 1.3.6.1.4.1.311.2.1.12
OIDSpcSPOpusInfoObjID = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 2, 1, 12}
)
// PKCS7 has SignData and signature information (signerInfo)
// Because Authenticode signatures support only one signer,
// digestAlgorithms must contain only one digestAlgorithmIdentifier structure
// and the structure must match the value set in the SignerInfo structure's digestAlgorithm field.
// If not, the signature has been tampered with.
type PKCS7 struct {
Raw interface{}
ContentType asn1.ObjectIdentifier // should be signedData
Version int // signedData version
DigestAlgorithmIdentifiers []pkix.AlgorithmIdentifier // used to sign the contents of the ContentInfo type
Certificates []*x509.Certificate // list of x509 certs
CRLs []pkix.CertificateList // CRLs
SignerInfos []signerInfo // list of signature information
}
type signerInfo struct {
Version int
IssuerAndSerialNumber issuerAndSerialNumber
DigestAlgorithm pkix.AlgorithmIdentifier
AuthenticatedAttribute authenticatedAttribute
DigestEncryptionAlgorithm pkix.AlgorithmIdentifier
EncryptedDigest []byte
UnauthenticatedAttribute unauthenticatedAttribute
}
type unauthenticatedAttribute struct {
Version int
IssuerAndSerialNumber issuerAndSerialNumber
DigestAlgorithm pkix.AlgorithmIdentifier
AuthenticatedAttribute authenticatedAttribute
DigestEncryptionAlgorithm pkix.AlgorithmIdentifier
MessageDigest []byte
}
type authenticatedAttribute struct {
ContentType asn1.ObjectIdentifier
MessageDigest []byte
SigningTime time.Time //this is only for unauthenticatedAttribute
}
// The issuer name and serial number of the signing certificate, as defined by ”PKCS #7: Cryptographic Message Syntax Standard
type issuerAndSerialNumber struct {
IssuerName pkix.RDNSequence
SerialNumber *big.Int
}
// ParsePKCS7 decodes a DER encoded PKCS7 package
// It returns a PKCS#7
func ParsePKCS7(asn1Data []byte) (*PKCS7, error) {
if len(asn1Data) == 0 {
return nil, errors.New("pkcs7 DER input data is empty")
}
var cInfo asn1ContentInfo
rest, err := asn1.Unmarshal(asn1Data, &cInfo)
if err != nil {
return nil, err
}
if len(rest) > 0 {
// fmt.Println(string(rest))
// return nil, asn1.SyntaxError{Msg: "trailing data"}
}
switch {
case cInfo.ContentType.Equal(OIDSignedData):
return parseSignedData(cInfo.Content.Bytes)
}
return nil, errors.New("supporting only a signedData format DER input now; other types are not supported yet")
}
// parseSignedData parses a PKCS7 signedData DER
// It returns a PKCS7
func parseSignedData(buf []byte) (*PKCS7, error) {
var pkcs7 PKCS7
var signedData asn1SignedData
rest, err := asn1.Unmarshal(buf, &signedData)
if err != nil {
return nil, err
}
if len(rest) > 0 {
return nil, asn1.SyntaxError{Msg: "trailing der bytes in the message"}
}
//x.509 certificates
certs, err := signedData.Certificates.Parse()
if err != nil {
return nil, err
}
pkcs7.Certificates = certs
//CRLs & contentType
pkcs7.Version = signedData.Version
pkcs7.CRLs = signedData.CRLs
pkcs7.ContentType = signedData.ContentInfo.ContentType
//DigestAlgorithmIdentifiers
for _, algo := range signedData.DigestAlgorithmIdentifiers {
pkcs7.DigestAlgorithmIdentifiers = append(pkcs7.DigestAlgorithmIdentifiers, algo)
}
//Signature information (signerinfo)
for _, s := range signedData.SignerInfos {
var signerInfo signerInfo
signerInfo.Version = s.Version
signerInfo.DigestAlgorithm = s.DigestAlgorithm
signerInfo.DigestEncryptionAlgorithm = s.DigestEncryptionAlgorithm
signerInfo.EncryptedDigest = s.EncryptedDigest
signerInfo.IssuerAndSerialNumber = s.IssuerAndSerialNumber
// authenticatedAttribute
var authenticatedAttr authenticatedAttribute
for _, attr := range s.AuthenticatedAttributes {
if attr.Type.Equal(OIDContentType) { //ContentType
var oid asn1.ObjectIdentifier
rest, err := asn1.Unmarshal(attr.Value.Bytes, &oid)
if err != nil {
return nil, err
}
if len(rest) > 0 {
return nil, asn1.SyntaxError{Msg: "trailing der bytes in the message"}
}
authenticatedAttr.ContentType = oid
} else if attr.Type.Equal(OIDMessageDigest) {
var msg []byte
rest, err := asn1.Unmarshal(attr.Value.Bytes, &msg)
if err != nil {
return nil, err
}
if len(rest) > 0 {
return nil, asn1.SyntaxError{Msg: "trailing der bytes in the message"}
}
authenticatedAttr.MessageDigest = msg
} else if attr.Type.Equal(OIDSpcSPOpusInfoObjID) {
//TODO: Implement to handle this OID
// fmt.Println(attr)
continue
} else if attr.Type.Equal(OIDSpcStatementTypeObjID) {
//TODO: Implement to handle this OID
// fmt.Println(attr)
continue
} else if attr.Type.Equal(OIDSigningTime) {
// this violates the authenticode specification
// signingTime should be specified at unauthenticatedAttributes
// but some old pkcs7 has this signing time in authenticatedAttributes
var t time.Time
rest, err = asn1.Unmarshal(attr.Value.Bytes, &t)
if err != nil {
return nil, err
}
if len(rest) > 0 {
return nil, asn1.SyntaxError{Msg: "trailing der bytes in the message"}
}
authenticatedAttr.SigningTime = t
}
}
signerInfo.AuthenticatedAttribute = authenticatedAttr
// Handles unauthenticated Attributes
// Note: a few of pkcs7 have two unauthenticated attributes
// because some publishers put their additional information into
// the second unauthenticated attribute.
// Otherwise, mostly pkcs7 has one unauthenticated attribute and
// the single attribute has the information about signing time, etc.
if len(s.UnauthenticatedAttributes) > 0 {
attr := s.UnauthenticatedAttributes[0]
var unAuthenticatedAttr unauthenticatedAttribute
var unAuthAttr asn1UnauthenticatedAttribute
rest, err := asn1.Unmarshal(attr.Value.Bytes, &unAuthAttr)
if err != nil {
return nil, err
}
if len(rest) > 0 {
return nil, asn1.SyntaxError{Msg: "trailing data"}
}
unAuthenticatedAttr.Version = unAuthAttr.Version
unAuthenticatedAttr.MessageDigest = unAuthAttr.MessageDigest
unAuthenticatedAttr.IssuerAndSerialNumber = unAuthAttr.IssuerAndSerialNumber
unAuthenticatedAttr.DigestEncryptionAlgorithm = unAuthAttr.DigestEncryptionAlgorithm
unAuthenticatedAttr.DigestAlgorithm = unAuthAttr.DigestAlgorithm
//authenticatedAttributes in unauthenticatedAttributes
//this attribute contains encrypted digest, signing time, contentype
buf := unAuthAttr.AuthenticatedAttributes.Bytes
for {
var at asn1Attribute
totalRest, err := asn1.Unmarshal(buf, &at)
if err != nil {
return nil, err
}
if at.Type.Equal(OIDContentType) {
var oid asn1.ObjectIdentifier
rest, err = asn1.Unmarshal(at.Value.Bytes, &oid)
if err != nil {
return nil, err
}
if len(rest) > 0 {
return nil, asn1.SyntaxError{Msg: "trailing der bytes in the message"}
}
// fmt.Printf("%s %s\n", at.Type, oid)
unAuthenticatedAttr.AuthenticatedAttribute.ContentType = oid
} else if at.Type.Equal(OIDSigningTime) {
var t time.Time
rest, err = asn1.Unmarshal(at.Value.Bytes, &t)
if err != nil {
return nil, err
}
if len(rest) > 0 {
return nil, asn1.SyntaxError{Msg: "trailing der bytes in the message"}
}
// fmt.Printf("%s %s\n", at.Type, t)
unAuthenticatedAttr.AuthenticatedAttribute.SigningTime = t
} else if at.Type.Equal(OIDMessageDigest) {
var msg []byte
rest, err = asn1.Unmarshal(at.Value.Bytes, &msg)
if err != nil {
return nil, err
}
if len(rest) > 0 {
return nil, asn1.SyntaxError{Msg: "trailing der bytes in the message"}
}
// fmt.Printf("%s %X\n", at.Type, msg)
unAuthenticatedAttr.AuthenticatedAttribute.MessageDigest = msg
}
if len(totalRest) <= 0 {
break
}
buf = totalRest
}
signerInfo.UnauthenticatedAttribute = unAuthenticatedAttr
}
pkcs7.SignerInfos = append(pkcs7.SignerInfos, signerInfo)
}
return &pkcs7, nil
}
//Parse parses the list of the certificates bytes in raw
func (raw asn1RawCertificates) Parse() ([]*x509.Certificate, error) {
if len(raw.RawCerts) == 0 {
return nil, errors.New("rawCertificates is empty")
}
var val asn1.RawValue
if _, err := asn1.Unmarshal(raw.RawCerts, &val); err != nil {
return nil, err
}
return x509.ParseCertificates(val.Bytes)
}