{"record":{"id":"00e6842f5de6e4d7","repo":"hashicorp/terraform","slug":"failed-to-create-cert-signer-q-s","errorCode":null,"errorMessage":"failed to create cert signer %q: %s","messagePattern":"failed to create cert signer %q: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/communicator/ssh/provisioner.go","lineNumber":415,"sourceCode":"func signCertWithPrivateKey(pk string, certificate string) (ssh.AuthMethod, error) {\n\trawPk, err := ssh.ParseRawPrivateKey([]byte(pk))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse private key %q: %s\", pk, err)\n\t}\n\n\tpcert, _, _, _, err := ssh.ParseAuthorizedKey([]byte(certificate))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse certificate %q: %s\", certificate, err)\n\t}\n\n\tusigner, err := ssh.NewSignerFromKey(rawPk)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create signer from raw private key %q: %s\", rawPk, err)\n\t}\n\n\tucertSigner, err := ssh.NewCertSigner(pcert.(*ssh.Certificate), usigner)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create cert signer %q: %s\", usigner, err)\n\t}\n\n\treturn ssh.PublicKeys(ucertSigner), nil\n}\n\nfunc readPrivateKey(pk string) (ssh.AuthMethod, error) {\n\t// We parse the private key on our own first so that we can\n\t// show a nicer error if the private key has a password.\n\tblock, _ := pem.Decode([]byte(pk))\n\tif block == nil {\n\t\treturn nil, errors.New(\"Failed to read ssh private key: no key found\")\n\t}\n\tif block.Headers[\"Proc-Type\"] == \"4,ENCRYPTED\" {\n\t\treturn nil, errors.New(\n\t\t\t\"Failed to read ssh private key: password protected keys are\\n\" +\n\t\t\t\t\"not supported. Please decrypt the key prior to use.\")\n\t}\n","sourceCodeStart":397,"sourceCodeEnd":433,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/communicator/ssh/provisioner.go#L397-L433","documentation":"ssh.NewCertSigner combines the parsed certificate and signer. This fails if the certificate was not signed by the private key provided (key/cert mismatch), if the certificate is expired or structurally invalid for signer construction, or if the type assertion pcert.(*ssh.Certificate) panics because ParseAuthorizedKey returned a non-certificate key type. SECURITY NOTE: the error interpolates usigner via %q.","triggerScenarios":"The private key and certificate do not correspond — the certificate was signed by a different CA key. Also triggered when the certificate's principal or critical options are incompatible. There is also a latent PANIC risk: pcert.(*ssh.Certificate) is an unchecked type assertion that will panic (not error) if ParseAuthorizedKey returns a non-*Certificate type.","commonSituations":"User has multiple key pairs and mismatches a private key with a certificate signed under a different key. Certificate was regenerated by the CA but the user still references the old private key. Certificate and key from different environments (staging vs prod CA).","solutions":["Ensure the private_key and certificate are a matched pair — the certificate must be signed by the corresponding CA, and the private key must be the one the certificate identifies.","Regenerate both the key pair and certificate together: ssh-keygen -t rsa -f id_rsa then ssh-keygen -s ca_key -I identity id_rsa.pub.","Verify the certificate's serial and key ID match the private key: ssh-keygen -L -f id_rsa-cert.pub.","If hitting a panic (not an error) from the type assertion, ensure the certificate value is actually a certificate, not a plain public key."],"exampleFix":"# before — mismatched key and cert from different sources\nconnection {\n  private_key = file(\"~/.ssh/key_a\")\n  certificate  = file(\"~/.ssh/key_b-cert.pub\")  # signed for a different key\n}\n\n# after — matched pair\ncd ~/.ssh\nssh-keygen -t rsa -f tf_key\nssh-keygen -s ca_key -I tf-identity tf_key.pub\nconnection {\n  private_key = file(\"~/.ssh/tf_key\")\n  certificate  = file(\"~/.ssh/tf_key-cert.pub\")\n}","handlingStrategy":"validation","validationCode":"// Verify the key/cert pair is consistent before calling NewCertSigner\nfunc validateKeyCertPair(pk, cert string) error {\n    rawPk, err := ssh.ParseRawPrivateKey([]byte(pk))\n    if err != nil {\n        return err\n    }\n    signer, err := ssh.NewSignerFromKey(rawPk)\n    if err != nil {\n        return err\n    }\n    parsedKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(cert))\n    if err != nil {\n        return err\n    }\n    sshCert, ok := parsedKey.(*ssh.Certificate)\n    if !ok {\n        return errors.New(\"not a certificate\")\n    }\n    // Check the certificate's signature key matches the public key of the signer\n    certPubKey := sshCert.SignatureKey.Marshal()\n    signerPubKey := signer.PublicKey().Marshal()\n    if !bytes.Equal(certPubKey, signerPubKey) {\n        return errors.New(\"certificate was signed for a different key than the private key provided\")\n    }\n    return nil\n}","typeGuard":"// Safe type assertion guard to prevent the panic in the original code\nfunc asCertificate(key ssh.PublicKey) (*ssh.Certificate, bool) {\n    c, ok := key.(*ssh.Certificate)\n    return c, ok\n}","tryCatchPattern":"// Replace the unsafe assertion pcert.(*ssh.Certificate) with:\nsshCert, ok := pcert.(*ssh.Certificate)\nif !ok {\n    return nil, errors.New(\"parsed key is not an SSH certificate\")\n}\nucertSigner, err := ssh.NewCertSigner(sshCert, usigner)\nif err != nil {\n    return nil, fmt.Errorf(\"cert signer creation failed (key/cert mismatch?): %w\", err)\n}","preventionTips":["Always generate the key and certificate as a matched pair from one CA.","Use a safe type assertion (comma-ok form) instead of a single-return assertion to avoid panics.","Verify the pair with ssh-keygen -L -f cert-file and compare the key ID.","Do not log signer or key objects in error messages."],"tags":["ssh","crypto","certificate","key-mismatch","panic-risk"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}