pkcs12.go

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