I'm writing a method in Java to read SSH public key in RFC 4253 format, which, in my case, can be either RSA, or DSS.

What I really need is to read exponent and modulus for RSA key and y, p, q, g values for DSS key, which, according to the standard are of type mpint (multiple precision integers).

So basically, what I'm doing, is reading an integer first (that becomes "length"), then reading as many bytes as this integer is, then converting this byte array to Java's BigInteger.

It works perfectly well for valid keys. But the problem appears if there's a malformed key where this first integer is some extremely big number (let's say Integer.MAX_VALUE). Then such an approach results in Requested array size exceeds VM limit exception or some other Java errors related to memory. What I want to do to solve this issue is pre-checking if the length-integer is in a valid range (smaller then some max value).

And here comes the question: what are the max values for exponent, modulus, y, p, q and g? Is there even any maximum for them?

  • 1
    $\begingroup$ Although surely not optimal, but no number can be bigger than p or the modulus. $\endgroup$
    – user27950
    Jul 30, 2018 at 10:39
  • $\begingroup$ This is why protocols should always include a limited set of valid key sizes / parameters... $\endgroup$
    – Maarten Bodewes
    Jul 31, 2018 at 4:22

2 Answers 2


The cryptosystems themselves have no limit. You can use a DSA or RSA system with any kind of key size.

However, generating the DSA domain parameters over 16Ki or generating an RSA key over 16Ki in size is a very CPU consuming operation. My computer generating a 17Ki RSA key pair as I type this, just to give bartonjs another anecdote to tell his grand-children one time ;)

Ah, there it is, the modulus is (in hex):


For practical purposes however the 4Ki and 16 Ki limit proposed by bartonjs would be fine.

Nobody would begrudge you if you only support key sizes that are multiples of 8 or even 32 either; there are a lot of implementations that do this; few would support RSA with a key size that cannot be divided by 8, even if that is perfectly possible.

For the SSH protocol, you have to look at section 5 of RFC 4251 which specifies the data types. Here mpint is a signed big endian value encoded as a string type. This string type itself is an octet string (byte array in Java) preceded by an unsigned 32 bit integer encoding the length.

That means that a 00 valued padding byte will be required at the left of the modulus. So you should allow 16Ki / 8 = 2Ki byte for each number plus the one padding byte (not including the 4 bytes overhead that is used to encode the size of the mpint itself). Here 1Ki = 1024 bytes (so 2Ki + 1= 2049 bytes would be the total size of the number).

Note that RSA signatures are encoded as unsigned octet string, so you would require just 2Ki bytes for a 16Ki signature.

As example you could give the value 2049 as value of mpIntMaxBytes:

 * Parses the <code>mpint</code> (multi-precission integer) SSH type
 * specified in RFC 4251 section 5. This method advances the position in the
 * buffer to the byte after the parsed <code>mpint</code>. If a
 * {@link SSHDataParseException} occurs then the buffer will have advanced
 * an unspecified number of times.
 * @param buf
 *            the buffer that contains the <code>mpint</code> encoding from
 *            the current position
 * @param mpIntMaxBytes
 *            the maximum number of bytes that the signed encoding of the
 *            <code>mpint</code> may use
 * @return the <code>mpint</code> as BigInteger
 * @throws SSHDataParseException
 *             if the <code>mpint</code> encoding is too large or if it is
 *             empty
public BigInteger parseMPInt(ByteBuffer buf, int mpIntMaxBytes)
        throws SSHDataParseException {
    if (mpIntMaxBytes < 1) {
        throw new IllegalArgumentException(
                "maxBytes should be larger than 0 to contain an mpint");

    long mpIntSize = parseStringSize(buf);
    if (mpIntSize == 0) {
        throw new SSHDataParseException(
                "A number consists of at least 1 byte (00h represents zero)");

    if (mpIntSize > mpIntMaxBytes) {
        throw new SSHDataParseException("Number exceeds maximum size");

    // get mpint from the buffer using newly generated array (BigInteger
    // does not allow reading from a specific offset)
    byte[] mpIntBuf = new byte[(int) mpIntSize];

    // an mpint is a signed big endian byte array, just like the default
    // BigInteger encoding
    return new BigInteger(mpIntBuf);

 * Parses the size of the (octet) string, where the size of the string is a
 * 32 bit uint.
 * @param buf
 *            the buffer that contains the size encoding from the current
 *            position
 * @return the size as a value between 0 and 2^32 - 1
public long parseStringSize(ByteBuffer buf) {
    return buf.getInt() & 0xFFFFFFFF;

public static class SSHDataParseException extends Exception {
    private static final long serialVersionUID = 1L;

    public SSHDataParseException(String message) {

Note that this code still doesn't check if the returned BigInteger is positive or negative, of a specific bit size, nor if the minimum encoding has been used. Parsing is validating until you see blue from exhaustion I'm afraid.

And to make matters worse, this is untested code, good luck!


For DSA/DSS the key sizes are restricted by FIPS 186-4, so nothing will ever exceed 4096-bit.

For RSA, anecdotally the largest size anyone supports is 16k-bit, so a Modulus value expressed in 2049 bytes (2048 plus one padding byte).

  • $\begingroup$ I guess I assumed the DER/BER encoding $\endgroup$
    – bartonjs
    Jul 31, 2018 at 4:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.