{"record":{"id":"c28e03699ee5edaf","repo":"signalapp/Signal-Server","slug":"key-id-08x-has-been-reserved-or-revoked-and-may-n","errorCode":null,"errorMessage":"Key ID %08x has been reserved or revoked and may not be used in new certificates.","messagePattern":"Key ID %08x has been reserved or revoked and may not be used in new certificates\\.","errorType":"console","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/workers/CertificateCommand.java","lineNumber":80,"sourceCode":"    System.out.println(\"Private key: \" + Base64.getEncoder().encodeToString(keyPair.getPrivateKey().serialize()));\n  }\n\n  private void runCertificateCommand(Namespace namespace) throws InvalidKeyException, org.signal.libsignal.protocol.InvalidKeyException {\n    if (namespace.getString(\"key\") == null) {\n      System.out.println(\"No key specified!\");\n      return;\n    }\n\n    if (namespace.getInt(\"keyId\") == null) {\n      System.out.print(\"No key id specified!\");\n      return;\n    }\n\n    ECPrivateKey key   = new ECPrivateKey(Base64.getDecoder().decode(namespace.getString(\"key\")));\n    int          keyId = namespace.getInt(\"keyId\");\n\n    if (RESERVED_CERTIFICATE_IDS.contains(keyId)) {\n      throw new IllegalArgumentException(\n          String.format(\"Key ID %08x has been reserved or revoked and may not be used in new certificates.\", keyId));\n    }\n\n    ECKeyPair keyPair = ECKeyPair.generate();\n\n    byte[] certificate = MessageProtos.ServerCertificate.Certificate.newBuilder()\n                                                                    .setId(keyId)\n                                                                    .setKey(ByteString.copyFrom(keyPair.getPublicKey().serialize()))\n                                                                    .build()\n                                                                    .toByteArray();\n\n    byte[] signature;\n    signature = key.calculateSignature(certificate);\n\n    byte[] signedCertificate = MessageProtos.ServerCertificate.newBuilder()\n                                                              .setCertificate(ByteString.copyFrom(certificate))\n                                                              .setSignature(ByteString.copyFrom(signature))\n                                                              .build()","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/workers/CertificateCommand.java#L62-L98","documentation":"CertificateCommand refuses to generate a new server certificate signing key certificate when the operator-supplied --keyId is in the RESERVED_CERTIFICATE_IDS set. Those IDs belong to previously issued or revoked certificate keys and reusing them would let new certificates masquerade as ones chained to a revoked key. The command fails fast with an IllegalArgumentException naming the offending ID.","triggerScenarios":"Running the `certificate` command (via Dropwizard's `run` → `runCertificateCommand`) with a `--keyId` numeric flag whose value appears in RESERVED_CERTIFICATE_IDS.","commonSituations":"Re-issuing certificates after a key rotation and picking the old key ID; copying a deployment script from an older environment that used a since-reserved ID; fat-fingering a small keyId like 1 or 2 that collides with reserved entries.","solutions":["Pick a fresh key ID not present in RESERVED_CERTIFICATE_IDS in CertificateCommand.java","Check the RESERVED_CERTIFICATE_IDS set in the source to see which values are blocked","Re-run the certificate command with the new --keyId value"],"exampleFix":"// before\n.//bin/textsecure-server certificate --keyId 2 --key <base64>\n// after\n./bin/textsecure-server certificate --keyId 7 --key <base64>","handlingStrategy":"validation","validationCode":"int keyId = namespace.getInt(\"keyId\");\nif (RESERVED_CERTIFICATE_IDS.contains(keyId)) {\n    throw new IllegalArgumentException(\"keyId \" + keyId + \" is reserved; choose another\");\n}","typeGuard":null,"tryCatchPattern":"try { runCertificateCommand(...); } catch (IllegalArgumentException e) { logger.error(\"keyId rejected: {}\", e.getMessage()); }","preventionTips":["Consult RESERVED_CERTIFICATE_IDS before choosing a keyId","Use high, non-colliding key IDs for new certificates","Document key ID allocations per environment"],"tags":["cli","certificates","security"],"backgroundTag":"invalid-cli-argument","analyzedSha":"100ab61c82627582c867d19e1c0561ba2781e927","analyzedAt":"2026-09-09T13:29:47.883Z","contentChangedAt":"2026-09-09T13:29:47.883Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}