{"record":{"id":"85c5a8598919c0f9","repo":"Netflix/chaosmonkey","slug":"tls-x509keypair-failed","errorCode":null,"errorMessage":"tls.X509KeyPair failed","messagePattern":"tls\\.X509KeyPair failed","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"spinnaker/spinnaker.go","lineNumber":79,"sourceCode":"type spinnakerInstance struct {\n\tName string\n}\n\n// getClient takes PKCS#12 data (encrypted cert data in .p12 format) and the\n// password for the encrypted cert, and returns an http client that does TLS client auth\nfunc getClient(pfxData []byte, password string) (*http.Client, error) {\n\tblocks, err := pkcs12.ToPEM(pfxData, password)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"pkcs.ToPEM failed\")\n\t}\n\n\t// The first block is the cert and the last block is the private key\n\tcertPEMBlock := pem.EncodeToMemory(blocks[0])\n\tkeyPEMBlock := pem.EncodeToMemory(blocks[len(blocks)-1])\n\n\tcert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"tls.X509KeyPair failed\")\n\t}\n\n\ttlsConfig := &tls.Config{\n\t\tCertificates: []tls.Certificate{cert},\n\t}\n\ttransport := &http.Transport{TLSClientConfig: tlsConfig}\n\treturn &http.Client{Transport: transport}, nil\n}\n\n// getClientX509 takes X509 data (Public and Private keys) and the\n// and returns an http client that does TLS client auth\nfunc getClientX509(x509Cert, x509Key string) (*http.Client, error) {\n\tcert, err := tls.LoadX509KeyPair(x509Cert, x509Key)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"tls.X509KeyPair failed\")\n\t}\n\ttlsConfig := &tls.Config{\n\t\tCertificates:       []tls.Certificate{cert},","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/Netflix/chaosmonkey/blob/eaa28fb761c0ebe8644d1333e5d164e9cc3071e9/spinnaker/spinnaker.go#L61-L97","documentation":"spinnaker.getClient converted the PKCS#12 data to PEM blocks and then called tls.X509KeyPair on the first block (assumed cert) and last block (assumed private key). This error means the pair failed to load: the PEM blocks do not contain a valid certificate/private key, they don't match, or the p12 contained additional blocks so the first/last heuristic picked the wrong ones.","triggerScenarios":"Calling spinnaker.New with a .p12 whose block layout isn't cert-first/key-last (e.g. p12 includes CA chain blocks or multiple certs), or a p12 that decodes but whose first/last blocks aren't a matching cert+key (e.g. key encrypted differently or blocks reordered).","commonSituations":"p12 exported with the full chain so blocks[0] is an intermediate CA, not the leaf cert; p12 with extra attributes producing >2 blocks; corrupted re-export where cert and key don't correspond.","solutions":["Re-export the .p12 containing exactly one certificate and its matching private key, with the leaf cert first (e.g. `openssl pkcs12 -export -inkey key.pem -in cert.pem -certfile fullchain.pem` and verify order, or exclude the chain)","Inspect the p12 contents (`openssl pkcs12 -in cert.p12 -nokeys` / `-nocerts`) and confirm block order: first = leaf cert, last = private key","Verify the private key in the p12 matches the certificate (compare public keys via openssl x509/pkey modulus)","As a workaround, use the x509 path instead: pass x509Cert/x509Key PEM file paths to spinnaker.New"],"exampleFix":"// before\nopenssl pkcs12 -export -out s.p12 -in fullchain.pem -inkey key.pem   # CA-first p12; blocks[0] is not the leaf cert\n// after\nopenssl pkcs12 -export -out s.p12 -in leaf-cert.pem -inkey key.pem   # leaf cert first, key last","handlingStrategy":"validation","validationCode":"blocks, err := pkcs12.ToPEM(pfx, password)\nif err != nil {\n\treturn err\n}\nif len(blocks) < 2 {\n\treturn fmt.Errorf(\"p12 must contain at least a cert and a key, got %d blocks\", len(blocks))\n}\n// Mirror the library's first=cert, last=key heuristic before calling New\ncertPEM := pem.EncodeToMemory(blocks[0])\nkeyPEM := pem.EncodeToMemory(blocks[len(blocks)-1])\nif _, err := tls.X509KeyPair(certPEM, keyPEM); err != nil {\n\treturn fmt.Errorf(\"p12 block layout incompatible: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"sp, err := spinnaker.New(endpoint, certPath, password, \"\", \"\", user)\nif err != nil && strings.Contains(err.Error(), \"tls.X509KeyPair failed\") {\n\treturn fmt.Errorf(\"p12 must have the leaf cert first and private key last; re-export it: %w\", err)\n}","preventionTips":["Export the p12 with exactly one leaf certificate first and one matching private key last (avoid embedding the full chain)","Verify cert/key correspondence offline: `openssl pkcs12 -in cert.p12 -clcerts -nokeys | openssl x509 -noout -modulus` vs key modulus","Use the x509 PEM path of spinnaker.New if your p12 has a complex block layout","Round-trip test the p12 with tls.X509KeyPair in a unit test before shipping configs"],"tags":["go","tls","certificate","pkcs12","mtls"],"backgroundTag":"tls-key-pair-mismatch","analyzedSha":"eaa28fb761c0ebe8644d1333e5d164e9cc3071e9","analyzedAt":"2026-09-03T17:04:39.020Z","contentChangedAt":"2026-09-03T17:04:39.020Z","schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}