{"record":{"id":"4abb12e421da2519","repo":"JeffreySu/WeiXinMPSDK","slug":"api-pem-base64-der","errorCode":null,"errorMessage":"品牌 API 密钥必须是有效的 PEM 或 Base64 DER。","messagePattern":"品牌 API 密钥必须是有效的 PEM 或 Base64 DER。","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Senparc.Weixin.TenPay/Senparc.Weixin.TenPayV3/HttpHandlers/TenPayBrandApiCredentials.cs","lineNumber":126,"sourceCode":"                    var trimmedLine = line.Trim();\n                    if (trimmedLine.StartsWith(\"-----\",\n                        StringComparison.Ordinal))\n                    {\n                        continue;\n                    }\n\n                    builder.Append(trimmedLine);\n                }\n            }\n\n            var normalized = builder.ToString();\n            try\n            {\n                Convert.FromBase64String(normalized);\n            }\n            catch (FormatException exception)\n            {\n                throw new ArgumentException(\n                    \"品牌 API 密钥必须是有效的 PEM 或 Base64 DER。\",\n                    parameterName, exception);\n            }\n\n            return normalized;\n        }\n    }\n}\n","sourceCodeStart":108,"sourceCodeEnd":135,"githubUrl":"https://github.com/JeffreySu/WeiXinMPSDK/blob/be573f6f94bdbf718dd5f6cdecb137fbc7ff651e/src/Senparc.Weixin.TenPay/Senparc.Weixin.TenPayV3/HttpHandlers/TenPayBrandApiCredentials.cs#L108-L135","documentation":"NormalizeKey strips PEM headers/footers/whitespace and then validates the result is valid Base64 (representing DER key bytes) by calling Convert.FromBase64String; on FormatException it throws ArgumentException stating the brand API key must be a valid PEM or Base64 DER. It prevents constructing credentials with a malformed key that would fail later at signature time.","triggerScenarios":"Passing a raw PEM including surrounding text the stripper can't handle, a truncated key, a key in PKCS#1 raw text without valid Base64 payload, a URL-safe Base64 key, or accidentally passing a password/passphrase instead of the key.","commonSituations":"Pasting the PEM file with '-----BEGIN ...-----' plus extra prose; copying only part of the key from a document; keys converted between formats incorrectly; storing the key with escaped newlines in JSON that weren't unescaped.","solutions":["Verify the key content between the BEGIN/END markers is valid Base64 (test with Convert.FromBase64String or a Base64 validator).","Re-export the key in PEM format and pass the full file contents, including -----BEGIN/END----- lines.","Check for JSON/YAML escaping issues that replaced newlines or introduced stray characters.","Ensure you are passing the key itself, not a passphrase or a certificate."],"exampleFix":"// before\nvar key = File.ReadAllText(\"brand_key.txt\"); // contains log notes around the PEM\n// after\nvar key = File.ReadAllText(\"apiclient_key.pem\"); // clean PEM file","handlingStrategy":"validation","validationCode":"bool IsValidBase64Key(string pem)\n{\n    var body = pem.Replace(\"-----BEGIN PRIVATE KEY-----\",\"\").Replace(\"-----END PRIVATE KEY-----\",\"\")\n                  .Replace(\"\\r\",\"\").Replace(\"\\n\",\"\").Trim();\n    try { Convert.FromBase64String(body); return body.Length > 0; }\n    catch (FormatException) { return false; }\n}","typeGuard":null,"tryCatchPattern":"try { creds = new TenPayBrandApiCredentials(mchId, serial, publicKey, privateKey); }\ncatch (ArgumentException ex) when (ex.Message.Contains(\"PEM\"))\n{ logger.LogError(ex, \"Brand key is not valid PEM/Base64 DER\"); throw; }","preventionTips":["Store keys as whole PEM files and load with File.ReadAllText, never copy-paste through editors that mangle whitespace.","Validate keys at startup with a Base64 sanity check.","Beware JSON/YAML escaping of newlines in embedded keys.","Ensure you pass the key, not a passphrase or certificate."],"tags":["configuration","format-validation","keys"],"backgroundTag":"invalid-argument-format","analyzedSha":"be573f6f94bdbf718dd5f6cdecb137fbc7ff651e","analyzedAt":"2026-09-12T10:01:50.733Z","contentChangedAt":"2026-09-12T10:01:50.733Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}