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} 361