...

Source file src/golang.org/x/crypto/pkcs12/pkcs12.go

Documentation: golang.org/x/crypto/pkcs12

     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.
    13  package pkcs12
    14  
    15  import (
    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  
    26  var (
    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  
    37  type pfxPdu struct {
    38  	Version  int
    39  	AuthSafe contentInfo
    40  	MacData  macData `asn1:"optional"`
    41  }
    42  
    43  type contentInfo struct {
    44  	ContentType asn1.ObjectIdentifier
    45  	Content     asn1.RawValue `asn1:"tag:0,explicit,optional"`
    46  }
    47  
    48  type encryptedData struct {
    49  	Version              int
    50  	EncryptedContentInfo encryptedContentInfo
    51  }
    52  
    53  type encryptedContentInfo struct {
    54  	ContentType                asn1.ObjectIdentifier
    55  	ContentEncryptionAlgorithm pkix.AlgorithmIdentifier
    56  	EncryptedContent           []byte `asn1:"tag:0,optional"`
    57  }
    58  
    59  func (i encryptedContentInfo) Algorithm() pkix.AlgorithmIdentifier {
    60  	return i.ContentEncryptionAlgorithm
    61  }
    62  
    63  func (i encryptedContentInfo) Data() []byte { return i.EncryptedContent }
    64  
    65  type safeBag struct {
    66  	Id         asn1.ObjectIdentifier
    67  	Value      asn1.RawValue     `asn1:"tag:0,explicit"`
    68  	Attributes []pkcs12Attribute `asn1:"set,optional"`
    69  }
    70  
    71  type pkcs12Attribute struct {
    72  	Id    asn1.ObjectIdentifier
    73  	Value asn1.RawValue `asn1:"set"`
    74  }
    75  
    76  type encryptedPrivateKeyInfo struct {
    77  	AlgorithmIdentifier pkix.AlgorithmIdentifier
    78  	EncryptedData       []byte
    79  }
    80  
    81  func (i encryptedPrivateKeyInfo) Algorithm() pkix.AlgorithmIdentifier {
    82  	return i.AlgorithmIdentifier
    83  }
    84  
    85  func (i encryptedPrivateKeyInfo) Data() []byte {
    86  	return i.EncryptedData
    87  }
    88  
    89  // PEM block types
    90  const (
    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.
    97  func 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.
   114  func 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  
   138  func 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  
   187  func 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.
   225  func 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  
   284  func 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  }
   361  

View as plain text