~juju-qa/ubuntu/xenial/juju/2.0-rc2

« back to all changes in this revision

Viewing changes to src/golang.org/x/crypto/pkcs12/pkcs12.go

  • Committer: Nicholas Skaggs
  • Date: 2016-09-30 14:39:30 UTC
  • mfrom: (1.8.1)
  • Revision ID: nicholas.skaggs@canonical.com-20160930143930-vwwhrefh6ftckccy
import upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2015 The Go Authors. All rights reserved.
 
2
// Use of this source code is governed by a BSD-style
 
3
// license that can be found in the LICENSE file.
 
4
 
 
5
// Package pkcs12 implements some of PKCS#12.
 
6
//
 
7
// This implementation is distilled from https://tools.ietf.org/html/rfc7292
 
8
// and referenced documents. It is intended for decoding P12/PFX-stored
 
9
// certificates and keys for use with the crypto/tls package.
 
10
package pkcs12
 
11
 
 
12
import (
 
13
        "crypto/ecdsa"
 
14
        "crypto/rsa"
 
15
        "crypto/x509"
 
16
        "crypto/x509/pkix"
 
17
        "encoding/asn1"
 
18
        "encoding/hex"
 
19
        "encoding/pem"
 
20
        "errors"
 
21
)
 
22
 
 
23
var (
 
24
        oidDataContentType          = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 1})
 
25
        oidEncryptedDataContentType = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 6})
 
26
 
 
27
        oidFriendlyName     = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 20})
 
28
        oidLocalKeyID       = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 21})
 
29
        oidMicrosoftCSPName = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 311, 17, 1})
 
30
)
 
31
 
 
32
type pfxPdu struct {
 
33
        Version  int
 
34
        AuthSafe contentInfo
 
35
        MacData  macData `asn1:"optional"`
 
36
}
 
37
 
 
38
type contentInfo struct {
 
39
        ContentType asn1.ObjectIdentifier
 
40
        Content     asn1.RawValue `asn1:"tag:0,explicit,optional"`
 
41
}
 
42
 
 
43
type encryptedData struct {
 
44
        Version              int
 
45
        EncryptedContentInfo encryptedContentInfo
 
46
}
 
47
 
 
48
type encryptedContentInfo struct {
 
49
        ContentType                asn1.ObjectIdentifier
 
50
        ContentEncryptionAlgorithm pkix.AlgorithmIdentifier
 
51
        EncryptedContent           []byte `asn1:"tag:0,optional"`
 
52
}
 
53
 
 
54
func (i encryptedContentInfo) Algorithm() pkix.AlgorithmIdentifier {
 
55
        return i.ContentEncryptionAlgorithm
 
56
}
 
57
 
 
58
func (i encryptedContentInfo) Data() []byte { return i.EncryptedContent }
 
59
 
 
60
type safeBag struct {
 
61
        Id         asn1.ObjectIdentifier
 
62
        Value      asn1.RawValue     `asn1:"tag:0,explicit"`
 
63
        Attributes []pkcs12Attribute `asn1:"set,optional"`
 
64
}
 
65
 
 
66
type pkcs12Attribute struct {
 
67
        Id    asn1.ObjectIdentifier
 
68
        Value asn1.RawValue `asn1:"set"`
 
69
}
 
70
 
 
71
type encryptedPrivateKeyInfo struct {
 
72
        AlgorithmIdentifier pkix.AlgorithmIdentifier
 
73
        EncryptedData       []byte
 
74
}
 
75
 
 
76
func (i encryptedPrivateKeyInfo) Algorithm() pkix.AlgorithmIdentifier {
 
77
        return i.AlgorithmIdentifier
 
78
}
 
79
 
 
80
func (i encryptedPrivateKeyInfo) Data() []byte {
 
81
        return i.EncryptedData
 
82
}
 
83
 
 
84
// PEM block types
 
85
const (
 
86
        certificateType = "CERTIFICATE"
 
87
        privateKeyType  = "PRIVATE KEY"
 
88
)
 
89
 
 
90
// unmarshal calls asn1.Unmarshal, but also returns an error if there is any
 
91
// trailing data after unmarshaling.
 
92
func unmarshal(in []byte, out interface{}) error {
 
93
        trailing, err := asn1.Unmarshal(in, out)
 
94
        if err != nil {
 
95
                return err
 
96
        }
 
97
        if len(trailing) != 0 {
 
98
                return errors.New("pkcs12: trailing data found")
 
99
        }
 
100
        return nil
 
101
}
 
102
 
 
103
// ConvertToPEM converts all "safe bags" contained in pfxData to PEM blocks.
 
104
func ToPEM(pfxData []byte, password string) ([]*pem.Block, error) {
 
105
        encodedPassword, err := bmpString(password)
 
106
        if err != nil {
 
107
                return nil, ErrIncorrectPassword
 
108
        }
 
109
 
 
110
        bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword)
 
111
 
 
112
        blocks := make([]*pem.Block, 0, len(bags))
 
113
        for _, bag := range bags {
 
114
                block, err := convertBag(&bag, encodedPassword)
 
115
                if err != nil {
 
116
                        return nil, err
 
117
                }
 
118
                blocks = append(blocks, block)
 
119
        }
 
120
 
 
121
        return blocks, nil
 
122
}
 
123
 
 
124
func convertBag(bag *safeBag, password []byte) (*pem.Block, error) {
 
125
        block := &pem.Block{
 
126
                Headers: make(map[string]string),
 
127
        }
 
128
 
 
129
        for _, attribute := range bag.Attributes {
 
130
                k, v, err := convertAttribute(&attribute)
 
131
                if err != nil {
 
132
                        return nil, err
 
133
                }
 
134
                block.Headers[k] = v
 
135
        }
 
136
 
 
137
        switch {
 
138
        case bag.Id.Equal(oidCertBag):
 
139
                block.Type = certificateType
 
140
                certsData, err := decodeCertBag(bag.Value.Bytes)
 
141
                if err != nil {
 
142
                        return nil, err
 
143
                }
 
144
                block.Bytes = certsData
 
145
        case bag.Id.Equal(oidPKCS8ShroundedKeyBag):
 
146
                block.Type = privateKeyType
 
147
 
 
148
                key, err := decodePkcs8ShroudedKeyBag(bag.Value.Bytes, password)
 
149
                if err != nil {
 
150
                        return nil, err
 
151
                }
 
152
 
 
153
                switch key := key.(type) {
 
154
                case *rsa.PrivateKey:
 
155
                        block.Bytes = x509.MarshalPKCS1PrivateKey(key)
 
156
                case *ecdsa.PrivateKey:
 
157
                        block.Bytes, err = x509.MarshalECPrivateKey(key)
 
158
                        if err != nil {
 
159
                                return nil, err
 
160
                        }
 
161
                default:
 
162
                        return nil, errors.New("found unknown private key type in PKCS#8 wrapping")
 
163
                }
 
164
        default:
 
165
                return nil, errors.New("don't know how to convert a safe bag of type " + bag.Id.String())
 
166
        }
 
167
        return block, nil
 
168
}
 
169
 
 
170
func convertAttribute(attribute *pkcs12Attribute) (key, value string, err error) {
 
171
        isString := false
 
172
 
 
173
        switch {
 
174
        case attribute.Id.Equal(oidFriendlyName):
 
175
                key = "friendlyName"
 
176
                isString = true
 
177
        case attribute.Id.Equal(oidLocalKeyID):
 
178
                key = "localKeyId"
 
179
        case attribute.Id.Equal(oidMicrosoftCSPName):
 
180
                // This key is chosen to match OpenSSL.
 
181
                key = "Microsoft CSP Name"
 
182
                isString = true
 
183
        default:
 
184
                return "", "", errors.New("pkcs12: unknown attribute with OID " + attribute.Id.String())
 
185
        }
 
186
 
 
187
        if isString {
 
188
                if err := unmarshal(attribute.Value.Bytes, &attribute.Value); err != nil {
 
189
                        return "", "", err
 
190
                }
 
191
                if value, err = decodeBMPString(attribute.Value.Bytes); err != nil {
 
192
                        return "", "", err
 
193
                }
 
194
        } else {
 
195
                var id []byte
 
196
                if err := unmarshal(attribute.Value.Bytes, &id); err != nil {
 
197
                        return "", "", err
 
198
                }
 
199
                value = hex.EncodeToString(id)
 
200
        }
 
201
 
 
202
        return key, value, nil
 
203
}
 
204
 
 
205
// Decode extracts a certificate and private key from pfxData. This function
 
206
// assumes that there is only one certificate and only one private key in the
 
207
// pfxData.
 
208
func Decode(pfxData []byte, password string) (privateKey interface{}, certificate *x509.Certificate, err error) {
 
209
        encodedPassword, err := bmpString(password)
 
210
        if err != nil {
 
211
                return nil, nil, err
 
212
        }
 
213
 
 
214
        bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword)
 
215
        if err != nil {
 
216
                return nil, nil, err
 
217
        }
 
218
 
 
219
        if len(bags) != 2 {
 
220
                err = errors.New("pkcs12: expected exactly two safe bags in the PFX PDU")
 
221
                return
 
222
        }
 
223
 
 
224
        for _, bag := range bags {
 
225
                switch {
 
226
                case bag.Id.Equal(oidCertBag):
 
227
                        if certificate != nil {
 
228
                                err = errors.New("pkcs12: expected exactly one certificate bag")
 
229
                        }
 
230
 
 
231
                        certsData, err := decodeCertBag(bag.Value.Bytes)
 
232
                        if err != nil {
 
233
                                return nil, nil, err
 
234
                        }
 
235
                        certs, err := x509.ParseCertificates(certsData)
 
236
                        if err != nil {
 
237
                                return nil, nil, err
 
238
                        }
 
239
                        if len(certs) != 1 {
 
240
                                err = errors.New("pkcs12: expected exactly one certificate in the certBag")
 
241
                                return nil, nil, err
 
242
                        }
 
243
                        certificate = certs[0]
 
244
 
 
245
                case bag.Id.Equal(oidPKCS8ShroundedKeyBag):
 
246
                        if privateKey != nil {
 
247
                                err = errors.New("pkcs12: expected exactly one key bag")
 
248
                        }
 
249
 
 
250
                        if privateKey, err = decodePkcs8ShroudedKeyBag(bag.Value.Bytes, encodedPassword); err != nil {
 
251
                                return nil, nil, err
 
252
                        }
 
253
                }
 
254
        }
 
255
 
 
256
        if certificate == nil {
 
257
                return nil, nil, errors.New("pkcs12: certificate missing")
 
258
        }
 
259
        if privateKey == nil {
 
260
                return nil, nil, errors.New("pkcs12: private key missing")
 
261
        }
 
262
 
 
263
        return
 
264
}
 
265
 
 
266
func getSafeContents(p12Data, password []byte) (bags []safeBag, updatedPassword []byte, err error) {
 
267
        pfx := new(pfxPdu)
 
268
        if err := unmarshal(p12Data, pfx); err != nil {
 
269
                return nil, nil, errors.New("pkcs12: error reading P12 data: " + err.Error())
 
270
        }
 
271
 
 
272
        if pfx.Version != 3 {
 
273
                return nil, nil, NotImplementedError("can only decode v3 PFX PDU's")
 
274
        }
 
275
 
 
276
        if !pfx.AuthSafe.ContentType.Equal(oidDataContentType) {
 
277
                return nil, nil, NotImplementedError("only password-protected PFX is implemented")
 
278
        }
 
279
 
 
280
        // unmarshal the explicit bytes in the content for type 'data'
 
281
        if err := unmarshal(pfx.AuthSafe.Content.Bytes, &pfx.AuthSafe.Content); err != nil {
 
282
                return nil, nil, err
 
283
        }
 
284
 
 
285
        if len(pfx.MacData.Mac.Algorithm.Algorithm) == 0 {
 
286
                return nil, nil, errors.New("pkcs12: no MAC in data")
 
287
        }
 
288
 
 
289
        if err := verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password); err != nil {
 
290
                if err == ErrIncorrectPassword && len(password) == 2 && password[0] == 0 && password[1] == 0 {
 
291
                        // some implementations use an empty byte array
 
292
                        // for the empty string password try one more
 
293
                        // time with empty-empty password
 
294
                        password = nil
 
295
                        err = verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password)
 
296
                }
 
297
                if err != nil {
 
298
                        return nil, nil, err
 
299
                }
 
300
        }
 
301
 
 
302
        var authenticatedSafe []contentInfo
 
303
        if err := unmarshal(pfx.AuthSafe.Content.Bytes, &authenticatedSafe); err != nil {
 
304
                return nil, nil, err
 
305
        }
 
306
 
 
307
        if len(authenticatedSafe) != 2 {
 
308
                return nil, nil, NotImplementedError("expected exactly two items in the authenticated safe")
 
309
        }
 
310
 
 
311
        for _, ci := range authenticatedSafe {
 
312
                var data []byte
 
313
 
 
314
                switch {
 
315
                case ci.ContentType.Equal(oidDataContentType):
 
316
                        if err := unmarshal(ci.Content.Bytes, &data); err != nil {
 
317
                                return nil, nil, err
 
318
                        }
 
319
                case ci.ContentType.Equal(oidEncryptedDataContentType):
 
320
                        var encryptedData encryptedData
 
321
                        if err := unmarshal(ci.Content.Bytes, &encryptedData); err != nil {
 
322
                                return nil, nil, err
 
323
                        }
 
324
                        if encryptedData.Version != 0 {
 
325
                                return nil, nil, NotImplementedError("only version 0 of EncryptedData is supported")
 
326
                        }
 
327
                        if data, err = pbDecrypt(encryptedData.EncryptedContentInfo, password); err != nil {
 
328
                                return nil, nil, err
 
329
                        }
 
330
                default:
 
331
                        return nil, nil, NotImplementedError("only data and encryptedData content types are supported in authenticated safe")
 
332
                }
 
333
 
 
334
                var safeContents []safeBag
 
335
                if err := unmarshal(data, &safeContents); err != nil {
 
336
                        return nil, nil, err
 
337
                }
 
338
                bags = append(bags, safeContents...)
 
339
        }
 
340
 
 
341
        return bags, password, nil
 
342
}