diff --git a/pkcs12/pkcs12.go b/pkcs12/pkcs12.go index eff9ad3..29235e3 100644 --- a/pkcs12/pkcs12.go +++ b/pkcs12/pkcs12.go @@ -267,6 +267,45 @@ func Decode(pfxData []byte, password string) (privateKey interface{}, certificat return } +// DecodeAll extracts all certificate and private keys from pfxData. +func DecodeAll(pfxData []byte, password string) (privateKeys []interface{}, certificates []*x509.Certificate, err error) { + encodedPassword, err := bmpString(password) + if err != nil { + return nil, nil, err + } + + bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword) + if err != nil { + return nil, nil, err + } + + for _, bag := range bags { + switch { + case bag.Id.Equal(oidCertBag): + certsData, err := decodeCertBag(bag.Value.Bytes) + if err != nil { + return nil, nil, err + } + certs, err := x509.ParseCertificates(certsData) + if err != nil { + return nil, nil, err + } + certificates = append(certificates, certs...) + + case bag.Id.Equal(oidPKCS8ShroundedKeyBag): + privateKey, err := decodePkcs8ShroudedKeyBag(bag.Value.Bytes, encodedPassword) + + if err != nil { + return nil, nil, err + } + + privateKeys = append(privateKeys, privateKey) + } + } + + return +} + func getSafeContents(p12Data, password []byte) (bags []safeBag, updatedPassword []byte, err error) { pfx := new(pfxPdu) if err := unmarshal(p12Data, pfx); err != nil { diff --git a/pkcs12/pkcs12_test.go b/pkcs12/pkcs12_test.go index 14dd2a6..cabaaeb 100644 --- a/pkcs12/pkcs12_test.go +++ b/pkcs12/pkcs12_test.go @@ -31,6 +31,34 @@ func TestPfx(t *testing.T) { } } +func TestPfxDecodeAll(t *testing.T) { + for commonName, base64P12 := range testdata { + p12, _ := base64.StdEncoding.DecodeString(base64P12) + + privs, certs, err := DecodeAll(p12, "") + + if err != nil { + t.Fatal(err) + } + + if len(privs) != 1 { + t.Errorf("expected 1 private key, but got %d", len(privs)) + } + + if len(certs) != 1 { + t.Errorf("expected 1 certificate, but got %d", len(certs)) + } + + if err := privs[0].(*rsa.PrivateKey).Validate(); err != nil { + t.Errorf("error while validating private key: %v", err) + } + + if certs[0].Subject.CommonName != commonName { + t.Errorf("expected common name to be %q, but found %q", commonName, certs[0].Subject.CommonName) + } + } +} + func TestPEM(t *testing.T) { for commonName, base64P12 := range testdata { p12, _ := base64.StdEncoding.DecodeString(base64P12)