{"record":{"id":"1a00daaf15b44e52","repo":"we-promise/sure","slug":"invalid-certificate","errorCode":"invalid_certificate","errorMessage":"Invalid private key in certificate: #{e.message}","messagePattern":"Invalid private key in certificate: #(.+?)","errorType":"exception","errorClass":"EnableBankingError","httpStatus":null,"severity":"error","filePath":"app/models/provider/enable_banking.rb","lineNumber":247,"sourceCode":"\n      # Otherwise pick the first progressively-shorter window that advances the\n      # window forward, skipping any window that is not newer than the current\n      # date_from. Moving strictly forward guarantees progress and termination.\n      FALLBACK_TRANSACTIONS_DATE_FROM_DAYS\n        .map { |days| days.days.ago.to_date }\n        .find { |candidate| current.nil? || candidate > current }\n    end\n\n    def safe_psu_headers(headers)\n      headers.except(\"Authorization\", :Authorization, \"Accept\", :Accept, \"Content-Type\", :\"Content-Type\")\n    end\n\n    def extract_private_key(certificate_pem)\n      # Extract private key from PEM certificate\n      OpenSSL::PKey::RSA.new(certificate_pem)\n    rescue OpenSSL::PKey::RSAError => e\n      Rails.logger.error \"Enable Banking: Failed to parse private key: #{e.message}\"\n      raise EnableBankingError.new(\"Invalid private key in certificate: #{e.message}\", :invalid_certificate)\n    end\n\n    def generate_jwt\n      now = Time.current.to_i\n\n      header = {\n        typ: \"JWT\",\n        alg: \"RS256\",\n        kid: application_id\n      }\n\n      payload = {\n        iss: \"enablebanking.com\",\n        aud: \"api.enablebanking.com\",\n        iat: now,\n        exp: now + 3600  # 1 hour expiry\n      }\n","sourceCodeStart":229,"sourceCodeEnd":265,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/enable_banking.rb#L229-L265","documentation":"Raised by Provider::EnableBanking#extract_private_key during initialize when OpenSSL::PKey::RSA.new rejects the client_certificate argument — the string passed as :client_certificate is not a parseable RSA private key PEM. The OpenSSL error message is preserved ('Invalid private key in certificate: ...') and logged. Every subsequent call would fail anyway, so construction aborts immediately.","triggerScenarios":"Passing the Enable Banking application's public certificate instead of its private key; a PEM block missing its BEGIN/END lines or with mangled newlines (copied through a channel that strips them); an EC or encrypted key instead of an RSA one; pasting the JSON bundle instead of the PEM.","commonSituations":"Copy-paste of credentials from the Enable Banking portal into .env or a settings form losing line breaks; storing the 'certificate' field when the portal's private key was downloaded separately; whitespace substitution in YAML/JSON configs.","solutions":["Read the logged OpenSSL message — 'Could not parse PKey' usually means wrong content, 'Could not find start line' means broken BEGIN/END header","Re-download the RSA private key PEM from the Enable Banking application settings and pass the full '-----BEGIN RSA PRIVATE KEY-----...-----END RSA PRIVATE KEY-----' block","Validate locally: openssl rsa -in key.pem -check -noout (add -passin pass:... if the key is encrypted — the client does not support passphrase-encrypted keys)","Ensure the stored value keeps newlines intact (store as a single-line secret and gsub('\\\\n', \"\\n\") before use, or store the multiline PEM directly)"],"exampleFix":"# before\nProvider::EnableBanking.new(application_id: app_id, client_certificate: ENV[\"EB_CERT\"]) # mangled value\n\n# after\nkey_pem = ENV[\"EB_PRIVATE_KEY\"].to_s.gsub(\"\\\\n\", \"\\n\")\nraise ArgumentError, \"EB_PRIVATE_KEY is not an RSA PEM\" unless key_pem.include?(\"BEGIN\")\nProvider::EnableBanking.new(application_id: app_id, client_certificate: key_pem)","handlingStrategy":"validation","validationCode":"def valid_enable_banking_rsa_key?(pem)\n  OpenSSL::PKey.read(pem).is_a?(OpenSSL::PKey::RSA)\nrescue OpenSSL::PKey::PKeyError\n  false\nend\n\n# use before building the client:\n# raise ArgumentError, \"private key is not a valid RSA PEM\" unless valid_enable_banking_rsa_key?(ENV[\"EB_PRIVATE_KEY\"].to_s.gsub(\"\\\\n\", \"\\n\"))","typeGuard":"def eb_invalid_certificate?(error)\n  error.is_a?(Provider::EnableBanking::EnableBankingError) && error.error_type == :invalid_certificate\nend","tryCatchPattern":"begin\n  Provider::EnableBanking.new(application_id: app_id, client_certificate: key_pem)\nrescue Provider::EnableBanking::EnableBankingError => e\n  raise unless e.error_type == :invalid_certificate\n  raise \"Enable Banking credentials misconfigured: paste the full RSA private key PEM (BEGIN/END lines included)\"\nend","preventionTips":["Validate the PEM with OpenSSL at settings-save time, not at first sync","Store the full PEM including BEGIN/END lines; normalize escaped newlines on read","Never store the portal's public certificate where the private key is expected","Test with `openssl rsa -in key.pem -check -noout` when copying credentials"],"tags":["enable-banking","openssl","pem","private-key","configuration"],"backgroundTag":"invalid-private-key","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}