signalapp/Signal-Server · error · IllegalArgumentException

Key ID %08x has been reserved or revoked and may not be…

Error message

Key ID %08x has been reserved or revoked and may not be used in new certificates.

What it means

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.

Solutions

  1. Pick a fresh key ID not present in RESERVED_CERTIFICATE_IDS in CertificateCommand.java
  2. Check the RESERVED_CERTIFICATE_IDS set in the source to see which values are blocked
  3. Re-run the certificate command with the new --keyId value

Example fix

// before
.//bin/textsecure-server certificate --keyId 2 --key <base64>
// after
./bin/textsecure-server certificate --keyId 7 --key <base64>
Defensive patterns

Strategy: validation

Validate before calling

int keyId = namespace.getInt("keyId");
if (RESERVED_CERTIFICATE_IDS.contains(keyId)) {
    throw new IllegalArgumentException("keyId " + keyId + " is reserved; choose another");
}

Try / catch

try { runCertificateCommand(...); } catch (IllegalArgumentException e) { logger.error("keyId rejected: {}", e.getMessage()); }

Prevention

When it happens

Trigger: Running the `certificate` command (via Dropwizard's `run` → `runCertificateCommand`) with a `--keyId` numeric flag whose value appears in RESERVED_CERTIFICATE_IDS.

Common situations: 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.

Understand the failure class

Background: "Unknown argument", "Invalid value", and "must be one of": invalid CLI argument errors explained — this error's family across 35 libraries.

Related errors


AI-assisted analysis of signalapp/Signal-Server@100ab61c82 (2026-09-09). Data as JSON: /api/errors/c28e03699ee5edaf. Report an issue: GitHub.

Appendix: source

Thrown at service/src/main/java/org/whispersystems/textsecuregcm/workers/CertificateCommand.java:80

    System.out.println("Private key: " + Base64.getEncoder().encodeToString(keyPair.getPrivateKey().serialize()));
  }

  private void runCertificateCommand(Namespace namespace) throws InvalidKeyException, org.signal.libsignal.protocol.InvalidKeyException {
    if (namespace.getString("key") == null) {
      System.out.println("No key specified!");
      return;
    }

    if (namespace.getInt("keyId") == null) {
      System.out.print("No key id specified!");
      return;
    }

    ECPrivateKey key   = new ECPrivateKey(Base64.getDecoder().decode(namespace.getString("key")));
    int          keyId = namespace.getInt("keyId");

    if (RESERVED_CERTIFICATE_IDS.contains(keyId)) {
      throw new IllegalArgumentException(
          String.format("Key ID %08x has been reserved or revoked and may not be used in new certificates.", keyId));
    }

    ECKeyPair keyPair = ECKeyPair.generate();

    byte[] certificate = MessageProtos.ServerCertificate.Certificate.newBuilder()
                                                                    .setId(keyId)
                                                                    .setKey(ByteString.copyFrom(keyPair.getPublicKey().serialize()))
                                                                    .build()
                                                                    .toByteArray();

    byte[] signature;
    signature = key.calculateSignature(certificate);

    byte[] signedCertificate = MessageProtos.ServerCertificate.newBuilder()
                                                              .setCertificate(ByteString.copyFrom(certificate))
                                                              .setSignature(ByteString.copyFrom(signature))
                                                              .build()

View on GitHub (pinned to 100ab61c82)