In: Dev , Python, RSA, Encryption On : Wed 16 May 2018

If you want to give me SSH access to your server, use this public key (line breaks were added to not mess up the formatting):

1 2 3 4 5 6 7 8 9 10 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQ +Ondergetekende+/+Ondergetekende+/+Ondergetekende+/+Ondergetekende+/+Ondergetekende+/ +Ondergetekende+/+Ondergetekende+/+Ondergetekende+/+Ondergetekende+/+Ondergetekende+/ +Ondergetekende+/+Ondergetekende+/+Ondergetekende+/+Ondergetekende+/+Ondergetekende+/ +Ondergetekende+/+Ondergetekende+/+Ondergetekende+/+Ondergetekende+/+Ondergetekende+/ K4CVktnyUNz5qrKzT3kJgXfktU9ufFqVLD/GqlDCV+5swUZohICigzegEE10B+FjzXAHltTrzdLPb+U7ibuRq o4E+XBQ7DyDhrzwYmAd1RZFJRK/Wwf16/UTsxdXtSD9sMYPaOz4q1CGaqD/Xg7hhcbd5umh/Epjyn3vp1V+U1 Ee40/3nWrs3kulLPpZJkCAwQ8/fp0wEkW3UCC+i1tR1M+RhlLi2LSpv4lxxxLWg2Zn7X4E6v0qjm8APisC5LK vMfCfFM1hf+aVn4VMK2A4U2/oxDTPW4N7pisi96N5MhUawuacH2j5jVUYydcuXDb0alwx7/aT981Z6swtIFbi Zj9w==

Notice the repeated /+Ondergetekende+/ ? Yes, that is intentional. My public key is clearly marked as being mine, and yes, I actually have the private key that goes with it.

A shallow primer on RSA public keys

An OpenSSH RSA public key contains a header, a number called E and a number called N. These parts are stored together in base64 encoding. E is a relatively small prime number, typically it’s just 65537. N is the product of two large prime numbers, called P and Q. These prime numbers (P and Q) are randomly chosen when the key is generated, and N is computed from that.

In a public key, N takes up the most space. Of the 724 characters in my public key, 686 are entirely devoted to storing N. This means that if we can choose N, we can influence what a significant portion of the base64 representation looks like. Obviously, we’re limited to base64 characters, but given that that’s the entire alphabet, the numbers and two filler characters, that still gives a lot of options.

Injecting arbitrary text

Of course, N needs to be a valid number for a public key, so we can’t just pick any N. But, we do have significant freedoms. Prime numbers are really common, and every prime number is valid for an RSA key. So if we manage to find two primes that multiply together to a number that’s close to N, we can control the first few digits, and its representation.

We start out with a freshly generated public key. We take the base64 representation and replace a part of it with our injected text. Of course, now the key is no longer valid. Also, it’s just a public key, and we also need a private key. To obtain those, we read the (now invalid) N from the public key. Remember that we need multiply two primes, P and Q to form N. To build the private key, we also need to know what those two primes are. One of these (P) primes can chosen at random, as long as it’s a prime number. The other (Q) can be estimated by dividing N over P. The result of this division is not likely to be prime, but if we can find a prime close to it, the value of P*Q is probably close enough to our N, that the injected text still remains there.

After we’ve found P and Q, there’s some supporting numbers we need to compute, in order to produce a full private and public key, but with a basic understanding of RSA math, that’s a trivial thing to do.

The code

As usual, the code is on github. The tool is capable of generating keys in OpenSSH and PEM format. The code depends on cryptography. Also, the code will run faster if you install gmpy2. Without it, generating a 4096 bit key takes 10 minutes, with it, it’s less than 10 seconds.

Is this safe?

Yes. I believe this tool produces keys that are as hard to crack as any other well-generated key.

As usual with cryptographic tools, you need to be weary of tools made by malicious or incompetent actors. Obviously I claim not to be a malicious actor, and as the code is just a few lines long, it shouldn’t be hard to verify my code doesn’t share your generated keys with the KGB.

While I’m quite familiar with the RSA math involved here, I’m not a true cryptography expert. My code actively tries to avoid biases in prime selection, I delegate as much as possible to trusted libraries, and my code adheres to all best practices I’m aware of. Still, cryptography is a complicated field where a small mistake can break your security easily. Proceed with caution.