From ceb98d5313ea6350e9a59c6079e14789f7abbdb5 Mon Sep 17 00:00:00 2001 From: Mark Prikhno Date: Fri, 20 Sep 2024 16:25:32 +0300 Subject: [PATCH 1/2] feat: add DecodeChains func for multiple key bags --- pkcs12.go | 154 +++++++++++++++++++++++++++++++++++++++++++++++++ pkcs12_test.go | 26 +++++++++ 2 files changed, 180 insertions(+) diff --git a/pkcs12.go b/pkcs12.go index 6c8afac..8cb3b09 100644 --- a/pkcs12.go +++ b/pkcs12.go @@ -18,6 +18,8 @@ package pkcs12 // import "software.sslmate.com/src/go-pkcs12" import ( + "bytes" + "crypto" "crypto/ecdsa" "crypto/rand" "crypto/rsa" @@ -392,6 +394,58 @@ func convertAttribute(attribute *pkcs12Attribute) (key, value string, err error) return key, value, nil } +func extractFriendlyname(bag safeBag) (string, error) { + for _, attribute := range bag.Attributes { + if attribute.Id.Equal(oidFriendlyName) { + if err := unmarshal(attribute.Value.Bytes, &attribute.Value); err != nil { + return "", err + } + value, err := decodeBMPString(attribute.Value.Bytes) + if err != nil { + return "", err + } + return value, nil + } + } + return "", errors.New("pkcs12: friendlyName attribute not found") +} + +func verifyKeyCert(privateKey crypto.PrivateKey, certificate *x509.Certificate) bool { + pk, ok := privateKey.(interface { + Public() crypto.PublicKey + }) + if !ok { + return false + } + publicKey, ok := pk.Public().(interface { + Equal(crypto.PublicKey) bool + }) + if !ok { + return false + } + if publicKey.Equal(certificate.PublicKey) { + return true + } + return false +} + +func issuedBy(subject, issuer *x509.Certificate) bool { + return bytes.Equal(subject.RawIssuer, issuer.RawSubject) && issuer.CheckSignature(subject.SignatureAlgorithm, subject.RawTBSCertificate, subject.Signature) == nil +} + +func buildChain(leaf *x509.Certificate, certs []*x509.Certificate) (certChain []*x509.Certificate) { + if len(certs) <= 1 { + return certs + } + for idx, cert := range certs { + if issuedBy(leaf, cert) { + certChain = append(certChain, cert) + certChain = append(certChain, buildChain(cert, certs[idx+1:])...) + } + } + return +} + // Decode extracts a certificate and private key from pfxData, which must be a DER-encoded PKCS#12 file. This function // assumes that there is only one certificate and only one private key in the // pfxData. Since PKCS#12 files often contain more than one certificate, you @@ -473,6 +527,106 @@ func DecodeChain(pfxData []byte, password string) (privateKey interface{}, certi return } +// A Chain represents a private key, a leaf certificate matching it, and the CA certificate chain. +// It also stores the friendlyName of the private key. +type Chain struct { + FriendlyName string + PrivateKey crypto.PrivateKey + Leaf *x509.Certificate + CACerts []*x509.Certificate +} + +// DecodeChains extracts Chains from pfxData, which must be a DER-encoded PKCS#12 file. The function +// assumes there is at least one private key with a friendlyName attribute and at least one matching certificate. +// The function ignores certificates that do not match any private keys, or are not part of any CA certificates chain. +func DecodeChains(pfxData []byte, password string) (chains []Chain, err error) { + encodedPassword, err := bmpStringZeroTerminated(password) + if err != nil { + return nil, err + } + + bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword, 1, 2) + if err != nil { + return nil, err + } + + // extract all bags + privateKeysAll := make(map[string]crypto.PrivateKey) + var certsAll []*x509.Certificate // do not store cert alias + for _, bag := range bags { + friendlyName, err := extractFriendlyname(bag) + if err != nil { + friendlyName = "" + } + switch { + case bag.Id.Equal(oidCertBag): + certsData, err := decodeCertBag(bag.Value.Bytes) + if err != nil { + return nil, err + } + certs, err := x509.ParseCertificates(certsData) + if err != nil { + return nil, err + } + if len(certs) != 1 { + err = errors.New("pkcs12: expected exactly one certificate in the certBag") + return nil, err + } + certsAll = append(certsAll, certs[0]) + case bag.Id.Equal(oidKeyBag): + privateKey, err := x509.ParsePKCS8PrivateKey(bag.Value.Bytes) + if err != nil { + return nil, err + } + pk, ok := privateKey.(crypto.PrivateKey) + if !ok { + return nil, fmt.Errorf("pkcs12: failed to get private key") + } + privateKeysAll[friendlyName] = pk + case bag.Id.Equal(oidPKCS8ShroundedKeyBag): + privateKey, err := decodePkcs8ShroudedKeyBag(bag.Value.Bytes, encodedPassword) + if err != nil { + return nil, err + } + pk, ok := privateKey.(crypto.PrivateKey) + if !ok { + return nil, fmt.Errorf("pkcs12: failed to get private key") + } + privateKeysAll[friendlyName] = pk + } + } + for pkAlias, pk := range privateKeysAll { + // initialize chains and match private keys to leaf certificates + chain := Chain{ + FriendlyName: pkAlias, + PrivateKey: pk, + } + var idx int + for idx, cert := range certsAll { + _ = idx + if verifyKeyCert(pk, cert) { + chain.Leaf = cert + break + } + } + // recursively build a chain for leaf certificate + caCerts := buildChain(chain.Leaf, append(certsAll[:idx], certsAll[idx+1:]...)) + chain.CACerts = caCerts + chains = append(chains, chain) + } + // verify chains + for _, chain := range chains { + if chain.PrivateKey == nil { + return nil, errors.New("pkcs12: private key missing") + } + if chain.Leaf == nil { + return nil, errors.New("pkcs12: certificate missing") + } + } + + return +} + // DecodeTrustStore extracts the certificates from pfxData, which must be a DER-encoded // PKCS#12 file containing exclusively certificates with attribute 2.16.840.1.113894.746875.1.1, // which is used by Java to designate a trust anchor. diff --git a/pkcs12_test.go b/pkcs12_test.go index f563577..1bace67 100644 --- a/pkcs12_test.go +++ b/pkcs12_test.go @@ -169,6 +169,32 @@ func TestPBES2_AES192CBC(t *testing.T) { } } +func Test_smth(t *testing.T) { + alias := "all" + base64P12 := `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` + p12, _ := base64.StdEncoding.DecodeString(base64P12) + chains, err := DecodeChains(p12, "password") + if err != nil { + t.Fatal(err) + } + if chains[0].FriendlyName != alias { + t.Errorf("unexpected private key friendly name, got %s, want %s", chains[0].FriendlyName, alias) + } + pk := chains[0].PrivateKey + cert := chains[0].Leaf + caCerts := chains[0].CACerts + rsaPk, ok := pk.(*rsa.PrivateKey) + if !ok { + t.Error("could not cast to rsa private key") + } + if !rsaPk.PublicKey.Equal(cert.PublicKey) { + t.Error("public key embedded in private key not equal to public key of certificate") + } + if len(caCerts) != 1 { + t.Errorf("unexpected # of caCerts: got %d, want 0", len(caCerts)) + } +} + var testdata = map[string]string{ // 'null' password test case "Windows Azure Tools": `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`, From 865ff551112d34fe0ce64eada5b063f4c8acdce1 Mon Sep 17 00:00:00 2001 From: Mark Prikhno Date: Fri, 27 Sep 2024 10:16:23 +0300 Subject: [PATCH 2/2] remove debug --- pkcs12_test.go | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/pkcs12_test.go b/pkcs12_test.go index 1bace67..f563577 100644 --- a/pkcs12_test.go +++ b/pkcs12_test.go @@ -169,32 +169,6 @@ func TestPBES2_AES192CBC(t *testing.T) { } } -func Test_smth(t *testing.T) { - alias := "all" - base64P12 := `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` - p12, _ := base64.StdEncoding.DecodeString(base64P12) - chains, err := DecodeChains(p12, "password") - if err != nil { - t.Fatal(err) - } - if chains[0].FriendlyName != alias { - t.Errorf("unexpected private key friendly name, got %s, want %s", chains[0].FriendlyName, alias) - } - pk := chains[0].PrivateKey - cert := chains[0].Leaf - caCerts := chains[0].CACerts - rsaPk, ok := pk.(*rsa.PrivateKey) - if !ok { - t.Error("could not cast to rsa private key") - } - if !rsaPk.PublicKey.Equal(cert.PublicKey) { - t.Error("public key embedded in private key not equal to public key of certificate") - } - if len(caCerts) != 1 { - t.Errorf("unexpected # of caCerts: got %d, want 0", len(caCerts)) - } -} - var testdata = map[string]string{ // 'null' password test case "Windows Azure Tools": `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`,