Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Encrypt RSA Private Key with AES 256 in Java

I am writing a secure file sharing application in Java. The general architecture looks like this:

  1. User wishes to encrypt a file for secure sharing between multiple users.
  2. The application generates a random UUID on the client and uses this as the AES 256 password, and encrypts the data with the UUID.
  3. The UUID is then RSA encrypted with each person's public key. Once per shared user.
  4. Each encrypted UUID packet is stored as part of the file in a custom file header.
  5. The file is then uploaded to a server where others can access it.
  6. The user's can each use their private key to read the AES encryption key and decrypt the file.

Here is the catch. The user's private key must be encrypted and stored on our servers in our database so that the files can be accessed from multiple locations. The private key will be encrypted with a user selected password on the client prior to being uploaded to the server.

I would like to do this using AES 256 bit encryption. And I would like to do the entire thing without relying on BouncyCastle libraries or any 3rd party libraries. It needs to use the standard Java 5 libraries, which is why I have chosen to use AES 256 encryption and RSA rather than something like PGP.

Can anyone find anything inherently insecure with this approach, or think of a more efficient way to do this?

Edit:

OK, I'm updating the question because all of the answers I am getting are suggesting that I not transmit the private key to the server. The reason I need the private key on the server is because the user's need to be able to access their data from multiple clients and multiple locations (ie: their iphone, their ipad, their work laptop, their home pc). They do not want to have to manage and copy their keys from device to device, which is even more insecure than storing their keys on our server because they would just end up emailing them to themselves at that point.

like image 676
Nobody Avatar asked Jul 17 '12 17:07

Nobody


People also ask

Does Java support AES 256?

Here padding is required and Java provides 3 alternatives. For encoding, the AES algorithm is repetitive in nature and supports 128, 192, and 256 bits.

Can RSA be used with AES?

DES or AES data-encrypting keys that are encrypted using an RSA public key can be exchanged safely between two systems. The sending system and the receiving system do not need to share a secret key to be able to exchange RSA-encrypted DES or AES data-encrypting keys.


2 Answers

The big problem with this is using UUIDs. Although UUIDs are (sort of) guaranteed to be unique, quite a bit of what they contain is quite predictable; substantial amounts remain constant across all the UUIDs generated on a single machine. As such, if a person gets access to (for example) their own key, they can probably guess many other people's keys fairly easily.

The other part that's problematic is storing user's private keys on the server. This makes the whole rest of the scheme relatively fragile, since access to those keys obviously gives access to all the rest of the data. It also (apparently) means you'll normally be decrypting the data on the server, so when the user accesses that data across the network, it'll either need to be re-encrypted for transmission, and decrypted on the users's machine, or else you'll be transmitting the data in the clear (thus rendering most of the encryption useless).

Edit: As to how I think I'd do this:

I'd have a list of public keys on the server. When a client wants to share a file with some other clients, it obtains the public keys for those clients from the server. It then generates a secure random key, and encrypts the data with that key. It then encrypts the random key with the public keys of all the other clients that are supposed to be able to access the data. Put those together into a stream, and transmit them to the server. The other clients can then download the stream, decrypt the key with their private key, and use that to decrypt the data itself.

This means each client's private key remains truly private -- it never has to leave their machine in any form. All they ever have to share with the rest of the world is their public key (which, by definition, shouldn't cause a security problem).

With that, the two obvious lines of attack are against the random number generator, and against RSA itself. For the random number generator, I'd use Java's SecureRandom -- this is exactly the sort of purpose for which it's intended, and if memory serves it's been pretty carefully examined and significant breaks against it seem fairly unlikely.

I won't try to comment on the security of RSA itself. For now, I think your primary concern is with the protocol, not the encryption algorithm proper. Suffice to say that if RSA were significantly broken, you'd obviously need to change your code, but you'd have a lot of company.

With this, it's pretty much up to the client to store their private keys securely. I like smart cards for that job, but there are quite a few alternatives. From the viewpoint of the server and protocol, it's no longer really a factor at all though.

Edit 2: As for dealing with multiple devices, I think I'd simply treat each device as a separate user, with its own public/private key pair. I'd then (probably) group those together by the actual users, so I can easily choose "Joe Blow" to give him access on all his devices -- but with a hierarchical display, I could also pretty easily restrict access to a subset of those, so if I want to share it with Joe on his office machine, but it's sensitive enough that I don't want it going where somebody might look over his shoulder while he looks at it, I can pretty easily do that too.

This keeps life simple for the users, but retains the same basic security model (i.e., private keys remain private).

like image 88
Jerry Coffin Avatar answered Oct 01 '22 10:10

Jerry Coffin


The scheme you outline is equivalent to CMS (the standard underlying S/MIME) and PGP; fundamentally, it is secure. In CMS, this mode is called "key transport". You could also use multi-party "key agreement," with an algorithm like DH or ECDH.

The only problem is that you are using poorly chosen keys for AES.

I can't think of any reason to use a random UUID, which contains non-random bits. Just use the normal key generation mechanism of the Java Cryptography Architecture. Keys, plaintext, and ciphertext should all be represented as byte sequences, unless you need to accommodate some external storage or transport that only accommodates text.

Iterable<Certificate> recipients = null;
KeyGenerator gen = KeyGenerator.getInstance("AES");
gen.init(256);
SecretKey contentEncryptionKey = gen.generateKey();

Initialize the AES cipher and let the provider choose an IV.

Cipher contentCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
contentCipher.init(Cipher.ENCRYPT_MODE, contentEncryptionKey);
AlgorithmParameters params = contentCipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();

For each recipient, initialize the RSA cipher and encrypt the AES key.

Cipher keyEncryptionCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
for (Certificate recipient : recipients) {
  keyEncryptionCipher.init(Cipher.WRAP_MODE, recipient);
  byte[] encryptedKey = keyEncryptionCipher.wrap(contentEncryptionKey);
  /* Store the encryptedKey with an identifier for the recipient... */
}
/* Store the IV... */ 
/* Encrypt the file... */

Having users select and remember passwords that give 256 bits of effective strength is unreasonable. To get that strength, you'd have to randomly choose passwords, encode them as text, and have users write them down on a card. If you really need that much strength, you could check out a smart-card–based solution for storing the users' RSA keys.

I'd highly recommend using a CMS library to store your files. It will increase your chances that the protocol you're using is safe, the code you are using has had more review, and that other tools, libraries, and systems can inter-operate with the encrypted messages. BouncyCastle's API is a little obscure, but it might be worth learning it.

(I can't remember if Java 5 supports "RSA/ECB/OAEPWithSHA-512AndMGF1Padding"; if it does, you should use that instead of PKCS1Padding.)

like image 27
erickson Avatar answered Oct 01 '22 11:10

erickson