{"record":{"id":"97500f15b556eb18","repo":"spring-projects/spring-security","slug":"expected-length-data-as-4-bytes","errorCode":null,"errorMessage":"Expected length data as 4 bytes","messagePattern":"Expected length data as 4 bytes","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"crypto/src/main/java/org/springframework/security/crypto/encrypt/RsaKeyHelper.java","lineNumber":271,"sourceCode":"\t\t}\n\t}\n\n\tprivate static void writeBigInteger(ByteArrayOutputStream stream, BigInteger num) throws IOException {\n\t\tint length = num.toByteArray().length;\n\t\tbyte[] data = new byte[4];\n\t\tdata[0] = (byte) ((length >> 24) & 0xFF);\n\t\tdata[1] = (byte) ((length >> 16) & 0xFF);\n\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":253,"sourceCodeEnd":286,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/crypto/src/main/java/org/springframework/security/crypto/encrypt/RsaKeyHelper.java#L253-L286","documentation":"readBigInteger reads a 4-byte big-endian length header from the SSH key blob stream. If fewer than 4 bytes remain, the blob is truncated/malformed and this IOException is thrown (often surfacing as the message-less RuntimeException at line 243).","triggerScenarios":"Parsing an SSH-RSA blob that ends right after the prefix or between fields — i.e. the decoded base64 payload is shorter than the SSH wire structure requires.","commonSituations":"Partially copied .pub key content, keys that were base64-decoded twice or incorrectly, or non-RSA blob formats (e.g. ed25519 bodies) whose layout misaligns parsing.","solutions":["Supply the complete, unmodified key string including the full base64 body.","Verify the key with ssh-keygen before use.","Ensure the algorithm token is 'ssh-rsa' so blob layout expectations match.","Handle the wrapped IOException via getCause() to give users a meaningful message."],"exampleFix":"// before\nString body = \"AAAAB\"; // truncated base64\nhelper.extractPublicKey(\"ssh-rsa \" + body);\n// after\nString body = Files.readString(Path.of(\"id_rsa.pub\")).split(\" \")[1]; // complete base64\nhelper.extractPublicKey(\"ssh-rsa \" + body);","handlingStrategy":"validation","validationCode":"boolean hasFullBlob(String base64Body) {\n    try {\n        byte[] b = Base64.getDecoder().decode(base64Body);\n        if (b.length < 15) return false;\n        int l = ((b[11] & 0xFF) << 24) | ((b[12] & 0xFF) << 16) | ((b[13] & 0xFF) << 8) | (b[14] & 0xFF);\n        return 11 + 4 + l <= b.length;\n    } catch (IllegalArgumentException ex) { return false; }\n}","typeGuard":null,"tryCatchPattern":"try {\n    RSAPublicKey pk = helper.extractPublicKey(key);\n} catch (RuntimeException e) {\n    if (e.getCause() instanceof IOException) {\n        throw new ConfigException(\"SSH key blob truncated: \" + e.getCause().getMessage());\n    }\n    throw e;\n}","preventionTips":["Never truncate base64 bodies when copying keys.","Strip whitespace consistently before decoding.","Use file-based key loading to avoid manual edits.","Fail fast with getCause() details."],"tags":["ssh-key","rsa","truncated-data","ioexception"],"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"}