{"record":{"id":"2df643d8da46fd18","repo":"spring-projects/spring-security","slug":"expected","errorCode":null,"errorMessage":"Expected ","messagePattern":"Expected ","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"crypto/src/main/java/org/springframework/security/crypto/encrypt/RsaKeyHelper.java","lineNumber":279,"sourceCode":"\t\tdata[2] = (byte) ((length >> 8) & 0xFF);\n\t\tdata[3] = (byte) (length & 0xFF);\n\t\tstream.write(data);\n\t\tstream.write(num.toByteArray());\n\t}\n\n\tprivate static byte[] readBigInteger(ByteArrayInputStream in) throws IOException {\n\t\tbyte[] b = new byte[4];\n\n\t\tif (in.read(b) != 4) {\n\t\t\tthrow new IOException(\"Expected length data as 4 bytes\");\n\t\t}\n\n\t\tint l = ((b[0] & 0xFF) << 24) | ((b[1] & 0xFF) << 16) | ((b[2] & 0xFF) << 8) | (b[3] & 0xFF);\n\n\t\tb = new byte[l];\n\n\t\tif (in.read(b) != l) {\n\t\t\tthrow new IOException(\"Expected \" + l + \" key bytes\");\n\t\t}\n\n\t\treturn b;\n\t}\n\n}\n","sourceCodeStart":261,"sourceCodeEnd":286,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/crypto/src/main/java/org/springframework/security/crypto/encrypt/RsaKeyHelper.java#L261-L286","documentation":"After reading the 4-byte length header, readBigInteger reads exactly l key bytes; if the stream has fewer bytes than the declared length, the blob is malformed and this IOException ('Expected <l> key bytes') is thrown.","triggerScenarios":"The SSH blob declares a field length (e.g. exponent or modulus size) larger than the remaining bytes — truncated base64 or a corrupted/edited key body.","commonSituations":"Copy/paste truncation of long RSA keys, line-wrapping tools inserting whitespace that broke base64 decoding assumptions, or corrupted uploads of authorized_keys content.","solutions":["Restore the full key from its source (regenerate with ssh-keygen if lost).","Strip all whitespace/newlines from the base64 body before parsing.","Confirm the key's integrity with ssh-keygen -l -f keyfile.","Catch the RuntimeException and inspect getCause() to surface this message to the user."],"exampleFix":"// before\nString key = pastedKey.replaceAll(\"\\\\s\", \"\").substring(0, 300); // truncated\nhelper.extractPublicKey(key);\n// after\nString key = pastedKey.trim();\nif (key.split(\"\\\\s+\")[1].length() >= 372) { // typical 2048-bit ssh-rsa body\n    helper.extractPublicKey(key);\n}","handlingStrategy":"validation","validationCode":"boolean lengthsConsistent(byte[] blob) {\n    int off = 11;\n    while (off + 4 <= blob.length) {\n        int l = ((blob[off] & 0xFF) << 24) | ((blob[off+1] & 0xFF) << 16) | ((blob[off+2] & 0xFF) << 8) | (blob[off+3] & 0xFF);\n        if (l < 0 || off + 4 + l > blob.length) return false;\n        off += 4 + l;\n        if (off == blob.length) return true;\n    }\n    return false;\n}","typeGuard":null,"tryCatchPattern":"try {\n    RSAPublicKey pk = helper.extractPublicKey(key);\n} catch (RuntimeException e) {\n    if (e.getCause() != null && e.getCause().getMessage().contains(\"key bytes\")) {\n        throw new ConfigException(\"Declared key length exceeds available bytes — key is truncated or corrupted\");\n    }\n    throw e;\n}","preventionTips":["Verify the full key with 'ssh-keygen -l -f' before storing it in config.","Avoid pasting keys through channels that trim long lines (chat, tickets).","Store keys verbatim in files and read them programmatically.","Validate base64 decodes to a structurally consistent blob pre-parse."],"tags":["ssh-key","rsa","truncated-data","length-mismatch"],"backgroundTag":"invalid-argument-format","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}