I'm confuse about KeyGenerator and encoded key in JCE - java

Now I'm studying about Cryptography and I'm interested in JCE but I am confused about the KeyGenerator class. I know this class can generate a key for a symmetric algorithm but I don't understand why I should use this class? since I can create a secret key by myself using SecretKeyFactory and use it to init the cipher object,right?
like this code below
// Create Key
DESKeySpec desKeySpec = new DESKeySpec(key);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
// Create Cipher
Cipher desCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
desCipher.init(Cipher.ENCRYPT_MODE, secretKey);
and then compare with this code
KeyGenerator generator = KeyGenerator.getInstance("AES", "BC");
generator.init(192);
Key encryptionKey = generator.generateKey();
What is the difference between them? Do they do the same thing?
Also, In what situation should I choose to use the "getEncoded()" method??

KeyGenerator is a convenience class for generating keys. You can tell it what algorithm you will be using in your Cipher and it can generate a key that matches the algorithm without you having to provide anything.
You can of course do this manually, but it takes a few steps as you have have demonstrated in your question (and you don't even cover generating the key data in byte[] key).

There are some JCE providers where keys are treated more securely than byte arrays in that keys are used to encrypt and decrypt data, but are themselves never present in memory in the clear. For example, Smart Card and HSM-backed providers work like that.
When you create a byte array and then construct the key out of it, you're explicitly loading it into memory. When you use a KeyGenerator, the key might be created in a secure storage and not present in memory, eliminating one attack vector.

Related

In decryption using Java Cipher with "AES/CBC/PKCS5Padding", must IV be specified? and only SecretKeyFactory can be used?

I read some examples about using Java Cipher to encrypt and decrypt data. For example:
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = new SecureRandom();
keyGenerator.init(256, secureRandom);
SecretKey secretKey = keyGenerator.generateKey();
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, secretKey);
I have 2 questions about the decryption process.
Although an IV is required, we can leave it implicit by using Cipher.init(Cipher.ENCRYPT_MODE, Key). A random IV will be automatically applied to it. However, in decryption mode, the same IV must be used. Does it mean only the Cipher.init(int opmode, Key key, AlgorithmParameters params) should be used and the IV should be get from the encryption, stored and passed here?
Beside the '''KeyGenerator''', I also saw the example to generate key with '''SecretKeyFactory''':
String key = ...
SecretKeyFactory factory = SecretKeyFactory.getInstance("DES");
SecretKeySpec keySpec = factory.generateSecret(new DESKeySpec(key));
I suppose I can use it for AES if I change last line to
SecretKeySpec keySpec = factory.generateSecret(new SecretKeySpec(key,"AES"));
I am confused about when to use SecretKeyFactory to generate a key and when to use KeyGenerator. It seems the latter is generate a random key, the former is generated from a given key material. So does it mean in decryption mode, only SecretKeyFactory can be used?
Does it mean only the Cipher.init(int opmode, Key key, AlgorithmParameters params) should be used and the IV should be get from the encryption, stored and passed here?
Yes, exactly that, unless you are able to communicate it in other ways. Generally though the IV is randomized during encryption, then prefixed to the ciphertext. For AES-CBC it is always the same size as the block size: 16 bytes.
So does it mean in decryption mode, only SecretKeyFactory can be used?
Yes, although for AES there is a neat little shortcut; you can simply do:
SecretKey aesKey = new SecretKeySpec(keyBytes, "AES");
and be done with it. This is because SecretKeySpec implements SecretKey. For 3DES keys that's not a good idea because it would mean that the parity bits of DES are not set correctly. However, modern keys such as AES keys and HMAC keys solely consist of random data, so for those it is fine. There is one caveat: it will be a problem if you try and generate a key in a hardware device that way: it must be kept in software.
Note that I won't delve too much in key management and how keys need to be created. I've answered that question here, although that answer is certainly far from complete. Heck you could use dice and share the numbers over the phone for all I care :)

How to encrypt a string in Java by just using a password? [duplicate]

This question already has answers here:
How to encrypt and decrypt String with my passphrase in Java (Pc not mobile platform)? [duplicate]
(7 answers)
Closed 2 years ago.
I'm developing an application where I need to encrypt text using a user-entered password (as a key) and store it. User can decrypt his data whenever he wants by entering the password. The security of the data should depend upon the complexity and length of the password.
I have used AES before but here I can't use it as the encryption key in AES has to be of a specific length. I want something where the encryption key could be of any length.
I just can't figure out which encryption method or algorithm to use.
Search for PBKDF - password based key derivation function. Please note - passwords are not keys (passwords are having different length and usually less entropy).
Though you may derive a key from the password. Commonly used password-based key derivation functions used today are PBKDF2, Argon2, BCrypt or SCrypt. Just search for them.
You may check some examples
IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(psswdSalt, PBKDF_INTERATIONS, ivParamSpec);
PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray());
SecretKeyFactory pbeKeyFactory =
SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_128");
SecretKey pbeKey = pbeKeyFactory.generateSecret(pbeKeySpec);
Cipher cipher = Cipher.getInstance(PBE_CIPHER_NAME);
cipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
In theory - you may use a simple hash (e.g. sha-256) and take only necessary number of bits to derive a key. The issue is that the password are usually not that random if it is easy for a human to remember; this means that they are vulnerable to brute-force attacks or dictionary attacks.

AES encryption in Java for given key

I am trying to do AES encryption in Java. I have the following function:
public static String encrypt(String plainText, String key) throws Exception {
if (plainText == null || plainText.length() == 0) {
return plainText;
}
// get aes key
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
// encrypt
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] bytes = cipher.doFinal(plainText.getBytes("UTF-8"));
//encode
String encoded = Base64.encodeToString(bytes, Base64.NO_PADDING | Base64.NO_WRAP);
return encoded;
}
My aim is to be able to encrypt some text with a given key the same way every time. That is, if I call this function with the same two parameters many times, I want the same string to be returned on every call. I'll say in advance, I know that is not how cryptography is supposed to be done, but I need this functionality for my project.
Unfortunately, that is not the case. The key generated in line 7 seems to encrypt my string differently every time around. I assume there is some kind of extra random automatic salting occurring on the lower levels of this library, preventing me from achieving my goal.
Does anyone know a way in Java how I could go about encrypting a given string with a given key to the same value every time? Thanks.
UPDATE/CLARIFICATION: This is not for security. This is for the encryption of data for it to be obfuscated for certain people that might come in contact with working on the app itself. The information is not highly sensitive, but I do want it encrypted and then decrypted by the same key. I have others working with me on this with libraries in their respective languages, e.g. Ruby, and their libraries allow them to encrypt a value with a given key the same way every time. We all want to use the same parameters of the same encryption algorithm: Key length: 128 bits
Mode of operation: CBC
Initialization Vector: None (all zeros)
Is it perhaps that if one does not set an initialization vector, it is randomly assigned? I've got to check that out.
Yes, Java - or rather the security provider supplying the AES in CBC mode implementation - may default to a random IV (which you then have to retrieve afterwards and include with your ciphertext) if you don't explicitly specify it you get secure, randomized ciphertext.
If you want to use a zero IV you will have to specify it explicitly:
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()]);
This is only slightly more secure than ECB mode, as any repetition in different messages in the initial ciphertext blocks will be instantly visible to an attacker.
If you want to have a more secure mode without random IV - which is required for CBC mode to obtain CPA security - then you could check out synthetic IV (SIV) mode or GCM-SIV. For those modes the entire message needs to be identical with a previous one to leak info to an attacker. It's however slowe, not included in the standard VM and the ciphertext could be larger than AES/CBC (inclusion of IV/tag vs padding requirements).

Trouble when generating a secretKey with Blowfish

So I'm trying to get a secretKey for Blowfish using bouncycastle as this:
SecretKeyFactory factory =SecretKeyFactory.getInstance("Blowfish", "BC");
but it throws the following exception:
Exception in thread "main" java.security.NoSuchAlgorithmException: no such algorithm: Blowfish for provider BC
at sun.security.jca.GetInstance.getService(GetInstance.java:87)
at javax.crypto.JceSecurity.getInstance(JceSecurity.java:96)
at javax.crypto.SecretKeyFactory.getInstance(SecretKeyFactory.java:204)
at Main.main(Main.java:112)
How do I solve this? (it also happens when I use "RC4" instead of "Blowfish", but it works with "DES" and "AES")
Unlike asymmetric keys which have internal structure, you don't really need a factory for 'raw' secret (symmetric) keys, only those that use a derivation algorithm (PBE*, PBKDF2, etc). The standard (SunJCE) factories for DES and DESEDE do check/fix length and parity FWLIW, but the BC ones (plus AES beginning with 1.56) don't even do that. RC4 and Blowfish (among others) accept variable-length keys with no constraints on the bits, so there's not really anything the factory could do even if it were provided.
Just put your key material in a SecretKeySpec and use that. Unlike the other *KeySpec types, SecretKeySpec 'is-a' SecretKey (to be exact implements that interface) and can be used where a SecretKey or appropriate Key is needed, particularly in Cipher.init(mode, key ...)
If you really want to generate a key, not use/convert an existing one, you don't want a factory anyway. In spite of the name, factories only convert, they don't create. To generate a key you either need a KeyGenerator (or for asymmetric KeyPairGenerator) and those DO exist for RC4 (aka ARC4) and BLOWFISH in BC, and also in standard SunJCE but using the names ARCFOUR and BLOWFISH; or (for raw symmetric) just use SecureRandom to generate the correct number of bytes, and use that.

How to implement Java 256-bit AES encryption with CBC

I've read the following threads and they've helped a little, but I'm looking for a little more info.
How to write AES/CBC/PKCS5Padding encryption and decryption with Initialization Vector Parameter for BlackBerry
Java 256bit AES Encryption
Basically, what I am doing is writing a program that will encrypt a request to be sent over TCP/IP, and then decrypted by a server program. The encryption will need to be AES, and doing some research I found out I need to use CBC and PKCS5Padding. So basically I need a secret key and an IV as well.
The application I'm developing is for a phone, so I want to use the java security packages to keep the size down. I've got the design done, but unsure of the implementation of the IV and the shared key.
Here's some code:
// My user name
byte[] loginId = "login".getBytes();
byte[] preSharedKey128 = "ACME-1234AC".getBytes();
byte[] preSharedKey192 = "ACME-1234ACME-1234A".getBytes();
// 256 bit key
byte[] preSharedKey256 = "ACME-1234ACME-1234ACME-1234".getBytes();
byte[] preSharedKey = preSharedKey256;
// Initialization Vector
// Required for CBC
byte[] iv ={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
IvParameterSpec ips = new IvParameterSpec(iv);
byte[] encodedKey = new byte[loginId.length + preSharedKey.length];
System.arraycopy(loginId, 0, encodedKey, 0, loginId.length);
System.arraycopy(preSharedKey, 0, encodedKey, loginId.length, preSharedKey.length);
// The SecretKeySpec provides a mechanism for application-specific generation
// of cryptography keys for consumption by the Java Crypto classes.
// Create a key specification first, based on our key input.
SecretKey aesKey = new SecretKeySpec(encodedKey, "AES");
// Create a Cipher for encrypting the data using the key we created.
Cipher encryptCipher;
encryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// Initialize the Cipher with key and parameters
encryptCipher.init(Cipher.ENCRYPT_MODE, aesKey, ips);
// Our cleartext
String clearString = "33,8244000,9999,411,5012022517,0.00,0,1,V330";
byte[] cleartext = clearString.getBytes();
// Encrypt the cleartext
byte[] ciphertext = encryptCipher.doFinal(cleartext);
// Now decrypt back again...
// Decryption cipher
Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// Initialize PBE Cipher with key and parameters
decryptCipher.init(Cipher.DECRYPT_MODE, aesKey, ips);
// Decrypt the cleartext
byte[] deciphertext = decryptCipher.doFinal(ciphertext);
In a nutshell what it should do is encrypt some message that can decrypted by the server without the server needing to get a key or IV from the phone. Is there a way I could do this where I could secure the IV and key on the phone, and still have the key and IV known by the server as well? Feel free to tell me to make things more clear if they're not.
There are a few problems with the code. First of all, you really should use a key generator to generate secret keys. Just using some text directly might work for some algorithms, but others have weak keys and so forth that need to be tested.
Even if you want to do password-based encryption, the password should be run through a key-derivation algorithm to produce a key, as shown in my answer to the question that you cited already.
Also, you shouldn't use the no-arg getBytes() method of String. This is platform dependent. If all of the strings that you are encoding contain only characters from the US-ASCII character set, make it clear by specifying that encoding explicitly. Otherwise, if the phone and server platforms use different character encodings, the key and IV won't turn out the same.
For CBC mode, it's best to use a new IV for every message you send. Usually, CBC IVs are generated randomly. Other modes like CFB and OFB require unique IVs for every message. IVs are usually sent with along the ciphertext—IVs don't need to be kept secret, but many algorithms will break if a predictable IV is used.
The server doesn't need to get the secret or IV directly from the phone. You can configure the server with the secret key (or password, from which the secret key is derived), but in many applications, this would be a bad design.
For example, if the application is going to be deployed to the phones of multiple people, it isn't a good idea for them to use the same secret key. One malicious user can recover the key and break the system for everyone.
A better approach is to generate new secret keys at the phone, and use a key agreement algorithm to exchange the key with the server. Diffie-Hellman key agreement can be used for this, or the secret key can be encrypted with RSA and sent to the server.
Update:
Diffie-Hellman in "ephemeral-static" mode (and "static-static" mode too, though that's less desirable) is possible without an initial message from the server to the phone, as long as the server's public key is embedded in the application.
The server public key doesn't pose the same risk as embedding a common secret key in the phone. Since it is a public key, the threat would be an attacker getting his hands on (or remotely hacking into) the phone and replacing the real public key with a fake key that allows him to impersonate the server.
Static-static mode could be used, but it's actually more complicated and a little less secure. Every phone would need its own unique key pair, or you fall back into the secret key problem. At least there would be no need for the server to keep track of which phone has which key (assuming there is some authentication mechanism at the application level, like a password).
I don't know how fast phones are. On my desktop, generating an ephemeral key pair takes about 1/3 of a second. Generating Diffie-Hellman parameters is very slow; you'd definitely want to re-use the parameters from the server key.
Done similar projects in a midlet before, I have following advice for you:
There is no secure way to store shared secret on the phone. You can use it but this falls into a category called Security through Obscurity. It's like a "key under mat" kind of security.
Don't use 256-bit AES, which is not widely available. You might have to install another JCE. 128-bit AES or TripleDES are still considered secure. Considering #1, you shouldn't worry about this.
Encryption using a password (different for each user) is much more secure. But you shouldn't use password as the key like you are showing in the example. Please use PBEKeySpec (password-based encryption) to generate the keys.
If you are just worried about MITM (man-in-the-middle) attacks, use SSL.

Categories