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