{"record":{"id":"82ce46169ba72a7b","repo":"spring-projects/spring-security","slug":"bad-salt-length","errorCode":null,"errorMessage":"Bad salt length","messagePattern":"Bad salt length","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCrypt.java","lineNumber":552,"sourceCode":"\t\tlong rounds;\n\t\tif (log_rounds < 4 || log_rounds > 31) {\n\t\t\tif (!for_check) {\n\t\t\t\tthrow new IllegalArgumentException(\"Bad number of rounds\");\n\t\t\t}\n\t\t\tif (log_rounds != 0) {\n\t\t\t\tthrow new IllegalArgumentException(\"Bad number of rounds\");\n\t\t\t}\n\t\t\trounds = 0;\n\t\t}\n\t\telse {\n\t\t\trounds = roundsForLogRounds(log_rounds);\n\t\t\tif (rounds < 16 || rounds > 2147483648L) {\n\t\t\t\tthrow new IllegalArgumentException(\"Bad number of rounds\");\n\t\t\t}\n\t\t}\n\n\t\tif (salt.length != BCRYPT_SALT_LEN) {\n\t\t\tthrow new IllegalArgumentException(\"Bad salt length\");\n\t\t}\n\n\t\tinit_key();\n\t\tekskey(salt, password, sign_ext_bug, safety);\n\t\tfor (int i = 0; i < rounds; i++) {\n\t\t\tkey(password, sign_ext_bug, safety);\n\t\t\tkey(salt, false, safety);\n\t\t}\n\n\t\tfor (int i = 0; i < 64; i++) {\n\t\t\tfor (int j = 0; j < (clen >> 1); j++) {\n\t\t\t\tencipher(cdata, j << 1);\n\t\t\t}\n\t\t}\n\n\t\tbyte[] ret = new byte[clen * 4];\n\t\tfor (int i = 0, j = 0; i < clen; i++) {\n\t\t\tret[j++] = (byte) ((cdata[i] >> 24) & 0xff);","sourceCodeStart":534,"sourceCodeEnd":570,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCrypt.java#L534-L570","documentation":"BCrypt.crypt_raw() requires the raw salt byte array to be exactly BCRYPT_SALT_LEN (16) bytes long. Any other length throws \"Bad salt length\". This guards the internal Blowfish key schedule which consumes exactly 16 salt bytes.","triggerScenarios":"Passing a salt byte array of length != 16 directly to BCrypt.hashpw's internal path / crypt_raw, e.g. a Base64-decoded salt with wrong padding or a raw password string used as salt.","commonSituations":"Manually decoding the salt portion of a $2a$ hash with a decoder that adds/strips padding, generating salt with another library producing non-16-byte salts, or truncating byte arrays.","solutions":["Generate salts with BCrypt.gensalt() instead of supplying your own bytes","If decoding the embedded salt, use BCrypt's decode_base64 semantics; a $2a$/$2b$ salt substring is 22 base64 chars = 16 bytes","Ensure your Base64 decode is unpadded bcrypt alphabet, not standard Base64"],"exampleFix":"// before\nbyte[] salt = Base64.getDecoder().decode(realSalt); // wrong alphabet, wrong length\n// after\nString saltStr = BCrypt.gensalt(12); // let the library create the salt","handlingStrategy":"validation","validationCode":"byte[] decoded = customDecode(realSalt);\nif (decoded.length != 16) {\n    throw new IllegalArgumentException(\"bcrypt salt must decode to 16 bytes\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    hash = BCrypt.hashpw(pw, salt);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().equals(\"Bad salt length\")) { /* regenerate salt via gensalt */ }\n}","preventionTips":["Use BCrypt.gensalt() for salt generation","Remember bcrypt's base64 alphabet differs from standard Base64 (no padding, ./ alphabet)"],"tags":["bcrypt","spring-security","crypto","salt","illegal-argument"],"backgroundTag":"invalid-argument-value","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}