apache/hadoop · error · IOException

Wrong key length. Required ${bitLength}, but got ${actualBit

Error message

Wrong key length. Required ${bitLength}, but got ${actualBitLength}

What it means

UserProvider.createKey enforces the same size invariant as the keystore provider: options.getBitLength() must equal 8 * material.length, so the bytes stored in the user's credential store always match the Metadata created for the key. The check runs before any credential entry is added, so a rejected call leaves no partial state.

Source

Thrown at hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/UserProvider.java:90

    }
    byte[] serialized = credentials.getSecretKey(new Text(name));
    if (serialized == null) {
      return null;
    }
    Metadata result = new Metadata(serialized);
    cache.put(name, result);
    return result;
  }

  @Override
  public synchronized KeyVersion createKey(String name, byte[] material,
                               Options options) throws IOException {
    Text nameT = new Text(name);
    if (credentials.getSecretKey(nameT) != null) {
      throw new IOException("Key " + name + " already exists in " + this);
    }
    if (options.getBitLength() != 8 * material.length) {
      throw new IOException("Wrong key length. Required " +
          options.getBitLength() + ", but got " + (8 * material.length));
    }
    Metadata meta = new Metadata(options.getCipher(), options.getBitLength(),
        options.getDescription(), options.getAttributes(), new Date(), 1);
    cache.put(name, meta);
    String versionName = buildVersionName(name, 0);
    credentials.addSecretKey(nameT, meta.serialize());
    credentials.addSecretKey(new Text(versionName), material);
    return new KeyVersion(name, versionName, material);
  }

  @Override
  public synchronized void deleteKey(String name) throws IOException {
    Metadata meta = getMetadata(name);
    if (meta == null) {
      throw new IOException("Key " + name + " does not exist in " + this);
    }
    for(int v=0; v < meta.getVersions(); ++v) {

View on GitHub (pinned to 2add963021)

Solutions

  1. Allocate material as new byte[options.getBitLength() / 8] and fill with SecureRandom
  2. Prefer generateKey(bitLength, cipher) or the one-arg createKey(name, options) overload
  3. Validate material.length == options.getBitLength() / 8 before the call

Example fix

// before
Options opts = new Options(conf).setCipher("AES").setBitLength(256);
byte[] material = new byte[16];
provider.createKey(name, material, opts);

// after
Options opts = new Options(conf).setCipher("AES").setBitLength(256);
byte[] material = KeyProvider.generateKey(opts.getBitLength(), "AES");
provider.createKey(name, material, opts);
Defensive patterns

Strategy: validation

Validate before calling

if (material == null || 8 * material.length != options.getBitLength()) {
  throw new IllegalArgumentException("material bits=" + (material == null ? 0 : 8 * material.length) + ", required=" + options.getBitLength());
}
provider.createKey(name, material, options);

Try / catch

catch (IOException e) { if (String.valueOf(e.getMessage()).startsWith("Wrong key length")) { material = KeyProvider.generateKey(options.getBitLength(), options.getCipher()); provider.createKey(name, material, options); } else { throw e; } }

Prevention

When it happens

Trigger: Options built with setBitLength(256) while passing a 16-byte array; 32 random bytes passed with default (128-bit) Options; generation code sized independently of the options object.

Common situations: Shared example code with hard-coded 16-byte material; upgrading key size without touching generation logic; tests using arbitrary strings as material.

Related errors


AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22). Data as JSON: /api/errors/b1d4641ce86c173a. Report an issue: GitHub.