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