{"record":{"id":"206149097519932b","repo":"router-for-me/CLIProxyAPI","slug":"home-ca-certificate-pem-is-invalid","errorCode":null,"errorMessage":"home ca certificate pem is invalid","messagePattern":"home ca certificate pem is invalid","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/home/certificate.go","lineNumber":202,"sourceCode":"func verifyCACertificatePEM(raw []byte, expectedFingerprint string) error {\n\tactual, errFingerprint := certificateFingerprintPEM(raw)\n\tif errFingerprint != nil {\n\t\treturn errFingerprint\n\t}\n\texpected := normalizeFingerprint(expectedFingerprint)\n\tif expected == \"\" {\n\t\treturn fmt.Errorf(\"home ca fingerprint is required\")\n\t}\n\tif actual != expected {\n\t\treturn fmt.Errorf(\"home ca fingerprint mismatch\")\n\t}\n\treturn nil\n}\n\nfunc certificateFingerprintPEM(raw []byte) (string, error) {\n\tblock, _ := pem.Decode(raw)\n\tif block == nil || block.Type != \"CERTIFICATE\" {\n\t\treturn \"\", fmt.Errorf(\"home ca certificate pem is invalid\")\n\t}\n\tcert, errParse := x509.ParseCertificate(block.Bytes)\n\tif errParse != nil {\n\t\treturn \"\", errParse\n\t}\n\tsum := sha256.Sum256(cert.Raw)\n\treturn hex.EncodeToString(sum[:]), nil\n}\n\nfunc normalizeFingerprint(fingerprint string) string {\n\tfingerprint = strings.TrimSpace(strings.ToLower(fingerprint))\n\tfingerprint = strings.ReplaceAll(fingerprint, \":\", \"\")\n\tfingerprint = strings.ReplaceAll(fingerprint, \" \", \"\")\n\treturn fingerprint\n}\n\nfunc loadOrCreateClientKey(path string) (*rsa.PrivateKey, error) {\n\tif fileExists(path) {","sourceCodeStart":184,"sourceCodeEnd":220,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/home/certificate.go#L184-L220","documentation":"Returned by certificateFingerprintPEM in internal/home/certificate.go when pem.Decode fails or the decoded block is not of type CERTIFICATE. The file passed as the home CA must be a PEM-encoded X.509 certificate; anything else (a key, a CSR, DER binary, text) is rejected before fingerprinting.","triggerScenarios":"Passing a DER-encoded .cer, a private key PEM, a CSR PEM, an empty or truncated file, or a non-PEM file as the home CA certificate path.","commonSituations":"Operator saved the wrong artifact during enrollment (client cert or key where CA belongs); file truncated by a partial copy or sync tool; Windows line-ending or BOM issues are usually fine but a double-extension file (ca.pem.txt) read raw fails; Docker volume mounted the directory instead of the file.","solutions":["Inspect the file: head -1 ca.pem must show -----BEGIN CERTIFICATE-----","If DER, convert: openssl x509 -in ca.der -inform DER -out ca.pem","Re-copy the CA from the home server enrollment output and verify with openssl x509 -in ca.pem -noout"],"exampleFix":"# before (DER file used directly)\nhome:\n  ca-cert: /etc/cliproxy/ca.der\n\n# after\nopenssl x509 -in /etc/cliproxy/ca.der -inform DER -out /etc/cliproxy/ca.pem\nhome:\n  ca-cert: /etc/cliproxy/ca.pem","handlingStrategy":"validation","validationCode":"func isPEMCertificate(pemBytes []byte) bool {\n    block, _ := pem.Decode(pemBytes)\n    return block != nil && block.Type == \"CERTIFICATE\"\n}\n\nif raw, err := os.ReadFile(cfg.CACert); err != nil || !isPEMCertificate(raw) {\n    return fmt.Errorf(\"ca-cert %s is not a PEM certificate\", cfg.CACert)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate PEM files in CI before deployment","Standardize on PEM output from your PKI tooling; convert DER once at packaging time"],"tags":["go","tls","certificate","pem","home"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}