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