{"record":{"id":"cad51f39d4e65cad","repo":"SonarSource/sonarqube","slug":"sign-requests-is-enabled-but-sonarqube-private-key","errorCode":null,"errorMessage":"Sign requests is enabled but SonarQube private key and/or SonarQube certificate is missing","messagePattern":"Sign requests is enabled but SonarQube private key and/or SonarQube certificate is missing","errorType":"validation","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SonarqubeRelyingPartyRegistrationRepository.java","lineNumber":93,"sourceCode":"    } catch (MalformedURLException | URISyntaxException | IllegalArgumentException e) {\n      throw new IllegalStateException(\"Invalid SAML Login URL\", e);\n    }\n  }\n\n  private void addSignRequestFieldsIfNecessary(RelyingPartyRegistration.Builder builder) {\n    //(on SQ) to sign request we need SP private key and certificate\n    //(on IDP) to verify request IDP needs SP public key (certificate)\n\n    //(on IDP) to sign response we need IDP private key (embedded)\n    //(on SQ) to verify response we need IDP public key (certificate) !mandatory!\n\n    //(on IDP) encryption: we need SP public key (certificate)\n    //(on SQ) decryption: we need Service Provide private key and certificate\n    Optional<String> serviceProviderPrivateKey = samlSettings.getServiceProviderPrivateKey();\n\n    if (serviceProviderPrivateKey.isEmpty() || samlSettings.getServiceProviderCertificate() == null) {\n      if (samlSettings.isSignRequestsEnabled()) {\n        throw new IllegalStateException(\"Sign requests is enabled but SonarQube private key and/or SonarQube certificate is missing\");\n      }\n      return;\n    }\n\n    String privateKeyString = serviceProviderPrivateKey.get();\n    String serviceProviderCertificateString = samlSettings.getServiceProviderCertificate();\n    PrivateKey privateKey = samlPrivateKeyConverter.toPrivateKey(privateKeyString);\n    X509Certificate spX509Certificate = samlCertificateConverter.toX509Certificate(serviceProviderCertificateString);\n    builder.decryptionX509Credentials(c -> c.add(Saml2X509Credential.decryption(privateKey, spX509Certificate)));\n\n    if (samlSettings.isSignRequestsEnabled()) {\n      builder.signingX509Credentials(c -> c.add(Saml2X509Credential.signing(privateKey, spX509Certificate)));\n    }\n  }\n\n  @VisibleForTesting\n  SamlSettings getSamlSettings() {\n    return samlSettings;","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-auth-saml/src/main/java/org/sonar/auth/saml/SonarqubeRelyingPartyRegistrationRepository.java#L75-L111","documentation":"addSignRequestFieldsIfNecessary() checks that both the SonarQube SP private key and certificate are configured. When either is missing AND 'Sign requests' (sonar.auth.saml.signatureEnabled) is enabled, SAML request signing cannot be set up on the RelyingPartyRegistration, so it throws IllegalStateException with this message.","triggerScenarios":"findByRegistrationId() builds the SAML registration while sonar.auth.saml.signatureEnabled=true but getServiceProviderPrivateKey() returns empty or getServiceProviderCertificate() returns null — i.e. signing was toggled on without uploading both the key and the certificate.","commonSituations":"Admins enabling 'Sign requests' in the SAML settings form but leaving the privateKey/certificate fields empty, configuring only the certificate (or only the key), or losing stored key settings after a SonarQube upgrade/migration.","solutions":["Upload BOTH sonar.auth.saml.privateKey (PKCS#8) and sonar.auth.saml.certificate in the SAML settings, then restart the analysis/reload settings","If you do not need request signing, set sonar.auth.saml.signatureEnabled=false (or uncheck 'Sign requests')","Re-export the key pair: openssl req -x509 -newkey rsa:2048 -nodes -keyout key.pem -out cert.pem, convert key to PKCS#8, and store both values"],"exampleFix":"// before: signing enabled with no key\nsonar.auth.saml.signatureEnabled=true\n// after: provide both parts\nsonar.auth.saml.signatureEnabled=true\nsonar.auth.saml.privateKey=MIIEvQIBADANBgkq...\nsonar.auth.saml.certificate=MIIDdzCCAl+gAwIBAg...","handlingStrategy":"validation","validationCode":"boolean signingOn = Boolean.parseBoolean(settings.get(\"sonar.auth.saml.signatureEnabled\"));\nboolean keyPresent = settings.get(\"sonar.auth.saml.privateKey\") != null && !settings.get(\"sonar.auth.saml.privateKey\").isBlank();\nboolean certPresent = settings.get(\"sonar.auth.saml.certificate\") != null && !settings.get(\"sonar.auth.saml.certificate\").isBlank();\nif (signingOn && !(keyPresent && certPresent)) {\n  throw new ConfigurationException(\"Sign requests requires BOTH privateKey and certificate\");\n}","typeGuard":"static boolean samlSigningConfigComplete(SamlSettings s) {\n  return !s.isSignRequestsEnabled() || (s.getServiceProviderPrivateKey().isPresent() && s.getServiceProviderCertificate() != null);\n}","tryCatchPattern":"try {\n  registration = repo.findByRegistrationId(\"sonarqube\");\n} catch (IllegalStateException e) {\n  if (e.getMessage().startsWith(\"Sign requests is enabled\")) {\n    throw new ConfigurationException(\"Upload SP key+cert or disable sonar.auth.saml.signatureEnabled\");\n  }\n  throw e;\n}","preventionTips":["Before toggling 'Sign requests' on, upload both the PKCS#8 private key and the X.509 certificate","Generate key+cert together: openssl req -x509 -newkey rsa:2048 -nodes","After SonarQube upgrades, re-verify SAML settings persisted correctly","If signing is not required by your IdP, keep sonar.auth.saml.signatureEnabled=false"],"tags":["saml","signing","missing-config"],"backgroundTag":"missing-required-config-field","analyzedSha":"184c821202192afc1c599fc912d0889b69fffa53","analyzedAt":"2026-09-09T12:23:51.573Z","contentChangedAt":"2026-09-09T12:23:51.573Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}