# Securely generating auth tokens

(Disclaimer: I'm an experienced server-side Java programmer, but with little crypto experience.)

Objective:

I've got a login server which, given a login+password pair, should issue a unique auth token. Using the same login+password pair multiple times should issue new unique tokens, all of them should be valid at the same time. A token is a ciphertext that only the login server should be able to create. Everyone else should only be able to extract the payload from it.

Possible solution:

First, I thought I should just use RSA to encrypt the payload with the private key. Other parties would just use the public key to decrypt it. But I read somewhere that RSA cipher doesn't provide integrity and authenticity so I figured I need to add an RSA signature, so it'd look like:

RSAcipher-with-private-key(payload-length + payload + RSAsignature(payload))


Then I discovered that the plaintext cannot be larger than the key size and thus I need a symmetric cipher, so I went researching on AES and stumbled upon GCM. As I understand, GCM solves the A. and I. problems, so I wouldn't need the signature anymore, and came up with that:

RSAcipher-with-private-key(AESkey + GCMiv) + AES-GCM-cipher(payload)


So the other parties would extract the AESkey and GCMiv using the public key, and then use that to decipher the AEScipher block.

I'm using BouncyCastle java library, since some people say AES/GCM in Java 1.8 is broken.

Will that work, is that secure, am I trying to invent a wheel here? If that's the case, what out of the box solution I could use?

• I don't know who thinks 'AES/GCM in Java 1.8 is broken' but it works perfectly for me, although Bouncy is also fine. There is one incompatibility: the 'Sun' provider uses GCMParameterSpec while Bouncy uses IvParameterSpec apparently for compatibility with older JREs (which didn't have GCM) -- which is a perfectly sensible goal for BC that is not a goal for Oracle. (And all of which is implementation and only matters after you get the design right.) – dave_thompson_085 Nov 20 '15 at 7:48

In asymmetric crypto including RSA, we ALWAYS encrypt with the public key, and decrypt with the private key (NEVER the other way around). In the question, what's wanted is to sign with the private key, not encrypt.

And that's enough to solve the whole problem, since RSA signature schemes exposed in BouncyCastle or the Java crypto API allow to sign data of arbitrary length.

Addition: The objective of encryption in modern cryptography is to prevent an adversary from gaining information about what's encrypted (with the exception of its length). When a piece of data is transformed into a cryptogram that can be returned to the original data using a public key, in such a way that only the holder of the private key could have prepared the cryptogram, that's not encryption; that's signature with message recovery.

In that name, signature covers that holding the private key is necessary to prepare the cryptogram, and that the private key allows to check the cryptogram. Message recovery covers that the cryptogram can be transformed back to the original message.

RSA signature schemes with message recovery (such as those in ISO/IEC 9796-2) have an advantage over those with appendix (such as those in PKCS#1): the size overhead for signing is smaller (slightly over a hash size, rather than a modulus size). For example, when using RSA-2048 and SHA-256, ISO/IEC 9796-2 embeds a 222-byte message into a 256-byte cryptogram, when PKCS#1 requires a 478-byte cryptogram (222 bytes for the message in clear, and 256 bytes for its signature).

As I understand it, the question attempts to make an hybrid signature scheme with message recovery, by mixing textbook RSA (used for signature with message recovery of a symmetric key), and AES/GCM (for integrity check and recovery of the bulk of the message). This is insecure to the utmost: an adversary (assumed to hold the public key, by definition of that) can determine the symmetric key from the RSA part, and then alter the message itself to whatever is desired.

Again, the problem at hand is finely solved with standard RSA signature. Appropriate schemes with appendix are directly available from the BouncyCastle or the Java crypto API. Message recovery could be used if reducing the size of the cryptogram by a few hundred bytes matters. While ECDSA signature (also readily available) would also reduce the signature overhead (to only about double that of RSA with message recovery), and significantly speed-up signature, it has the disadvantage that checking a cryptogram is significantly slower than with RSA.

• crypto.stackexchange.com/questions/2123/… looks like it'd work though? – Yuri Ushakov Nov 18 '15 at 20:26
• Also, I don't need to sign, I need to encrypt something that only I can encrypt, but everyone else can decrypt. That's, like, signature with reverse data retrieval I guess. – Yuri Ushakov Nov 18 '15 at 20:48
• @YuriUshakov, if everyone can decrypt it why bother encrypting at all? Just sign the token and provide everyone with the token + the signature. – SEJPM Nov 18 '15 at 21:57
• @fgrieu Exactly why I posted the question, thank you so much. I'll be looking into signatures with recovery and yes, I missed the part where the attacker could modify the AES-encrypted part. Cheers. – Yuri Ushakov Nov 19 '15 at 12:57

This sounds like Kerberos. ( https://en.wikipedia.org/wiki/Kerberos_%28protocol%29 )

In any case, you didn't mention, but it would seem quite important, how long are the generated auth tokens valid for, or how would you expire one. There is no such thing (IMHO) as permanent/indefinite authorization--if you believe otherwise, you should not be doing security work.

So being that you need a means to expire or revoke the tokens, they could be virtually any random number at all. Generate a random SALT and hash it would work just as well as anything. The thing you need in this case is a database of known good user ID and token pairs, presumably along with an expiry date.

• You are correct, I do have a database of known tokens, and the actual payload is user_id + token_expire_date. I will read up on Kerberos, thanks. – Yuri Ushakov Nov 18 '15 at 20:23
• Well if you have a database, just put the expiry date in the database, do not take the chance that an attacker could manipulate it by letting them submit it back to you. – P Holder Nov 19 '15 at 0:39