hashicorp/terraform · error

failed to parse certificate %q: %s

Error message

failed to parse certificate %q: %s

What it means

Raised in signCertWithPrivateKey when ssh.ParseAuthorizedKey fails parsing the certificate string. This occurs in the certificate-based auth path (both private_key and certificate configured). The certificate must be a valid SSH public key certificate in authorized_keys format for the cert signer to be constructed.

Source

Thrown at internal/communicator/ssh/provisioner.go:405

	}

	if opts.sshAgent != nil {
		conf.Auth = append(conf.Auth, opts.sshAgent.Auth())
	}

	return conf, nil
}

// Create a Cert Signer and return ssh.AuthMethod
func signCertWithPrivateKey(pk string, certificate string) (ssh.AuthMethod, error) {
	rawPk, err := ssh.ParseRawPrivateKey([]byte(pk))
	if err != nil {
		return nil, fmt.Errorf("failed to parse private key %q: %s", pk, err)
	}

	pcert, _, _, _, err := ssh.ParseAuthorizedKey([]byte(certificate))
	if err != nil {
		return nil, fmt.Errorf("failed to parse certificate %q: %s", certificate, err)
	}

	usigner, err := ssh.NewSignerFromKey(rawPk)
	if err != nil {
		return nil, fmt.Errorf("failed to create signer from raw private key %q: %s", rawPk, err)
	}

	ucertSigner, err := ssh.NewCertSigner(pcert.(*ssh.Certificate), usigner)
	if err != nil {
		return nil, fmt.Errorf("failed to create cert signer %q: %s", usigner, err)
	}

	return ssh.PublicKeys(ucertSigner), nil
}

func readPrivateKey(pk string) (ssh.AuthMethod, error) {
	// We parse the private key on our own first so that we can
	// show a nicer error if the private key has a password.

View on GitHub (pinned to c9def3e214)

Solutions

  1. Verify the certificate value is an actual SSH certificate (not a plain public key) — it should have '-cert' in the key type prefix.
  2. Ensure the certificate file path is correct (typically id_rsa-cert.pub or id_ed25519-cert.pub).
  3. Confirm the certificate content is complete and untruncated.
  4. Check with ssh-keygen -L -f certfile that the certificate is valid.

Example fix

// before
connection {
  private_key = file("~/.ssh/id_rsa")
  certificate = file("~/.ssh/id_rsa.pub") # wrong: this is a public key, not a cert
}

// after
connection {
  private_key = file("~/.ssh/id_rsa")
  certificate = file("~/.ssh/id_rsa-cert.pub")
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate the certificate is a real SSH certificate (not a plain public key)
func validateSSHCertificate(cert string) error {
    pk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(cert))
    if err != nil {
        return fmt.Errorf("certificate is not parseable: %w", err)
    }
    // A real certificate has a CertType; a plain public key does not
    if _, ok := pk.(*ssh.Certificate); !ok {
        return errors.New("the certificate value is a plain public key, not an SSH certificate")
    }
    return nil
}

Type guard

// Type guard: check if the parsed key is actually a certificate
func isSSHCertificate(certStr string) bool {
    pk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(certStr))
    if err != nil {
        return false
    }
    _, ok := pk.(*ssh.Certificate)
    return ok
}

Prevention

When it happens

Trigger: The certificate value is malformed, is a plain public key rather than a certificate, is truncated/corrupted, or is in an unsupported format. ParseAuthorizedKey expects the authorized_keys text format including the key type prefix.

Common situations: The certificate attribute points to the wrong file (e.g. a .pub key instead of a -cert.pub certificate), the certificate is expired or revoked (though parsing would still succeed), the cert content was truncated during copy-paste, or the format does not include the 'ssh-rsa-cert-v01@openssh.com' type prefix.

Understand the failure class

Related errors


AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07). Data as JSON: /api/errors/6b139cda05b0840f. Report an issue: GitHub.