Why are these square symbols appended after decrypting with bouncy castle? - java

I have created a simple java method that encrypts and decrypts text using the bouncy castle library. Encryption works as expected but when I decrypt something I get these extra square symbols at the end:
I think this might be something to do with padding but I've followed the example featured on bouncy castle's website, so I really can't understand why I would be getting this sort of output. Here is the code I am using:
[Main]
public static void main(String[] argv) {
String ciphertext = "PlJR5pzbowsuzHIc9iTKHg==";
String decrypted;
CryptoCodec codec = new CryptoCodec();
decrypted = codec.exec("AES", "xxxxooooxxxxoooo", ciphertext, false);
System.out.println("Ciphertext: " + ciphertext);
System.out.println("Decrypted: " + decrypted);
}
[CryptoCodec]
// Eod: (true) Encrypt or (false) decrypt.
public String exec(String algorithm, String key, String data, boolean eod) {
// Using AESEngine();
BlockCipher engine = CipherEngine.getBlockCipher(algorithm);
BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(engine));
byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
byte[] dataBytes;
if(eod) {
dataBytes = data.getBytes(StandardCharsets.UTF_8);
} else {
dataBytes = Base64.decode(data);
}
cipher.init(eod, new KeyParameter(keyBytes));
byte[] outputText = new byte[cipher.getOutputSize(dataBytes.length)];
int outputTextLen = cipher.processBytes(dataBytes, 0, dataBytes.length, outputText, 0);
try {
cipher.doFinal(outputText, outputTextLen);
} catch (CryptoException err) {
err.printStackTrace();
}
if(eod) {
return new String(Base64.encode(outputText));
} else {
return new String(outputText);
}
}
Please keep in mind I am still learning about cryptography and would love to hear any sort of explanation to why this may be happening. Thanks in advance.

During decryption cipher.getOutputSize(dataBytes.length) doesn't know how many bytes it will remove from padding (it doesn't even know that you're telling it about the last part of the data). So it tells you the maximum it could be.
Your destination array is therefore larger than it needs to be, and you need to respect how much data got filled in.
How do you know how much got filled in? Capture the return value from doFinal. What do you do with it then? Tell the String constructor when to stop reading.
You then end up with something like
try {
outputTextLen += cipher.doFinal(outputText, outputTextLen);
} catch (CryptoException err) {
err.printStackTrace();
}
if(eod) {
return new String(Base64.encode(outputText));
} else {
return new String(outputText, 0, outputTextLen);
}
Which also fixes your bug that if you encrypt 16 bytes of data right now you won't decrypt successfully.

Related

BASE64DecoderStream.decode returns different values on different computers

I have this java function that receives a string (str) and decodes it:
public static String decrypt(String str) {
try {
byte[] decodedKey = Base64.getDecoder().decode("seluR/beWhR=");
key = new SecretKeySpec(decodedKey, 0, decodedKey.length, "DES");
dcipher = Cipher.getInstance("DES");
dcipher.init(Cipher.DECRYPT_MODE, key);
byte[] dec = BASE64DecoderStream.decode(str.getBytes());
byte[] utf8 = dcipher.update(dec, 0, dec.length);
return new String(utf8, "UTF8");
} catch (Exception e) {
System.out.println(e);
}
return null;
}
When the function receives this value: JLMcNWcDwNJS1MVO3Urhgco2ZQzk2EBc
Returns this from my computer: 29/07/2022 16:19
Returns this from my client's computer: 29/07/2022 16:19:10.12
As you can see, values are very similar but they're different (the latter has 6 extra characters)
I don't understand why is this happening. The values should be exactly the same, isn't it? Both computers use the same JDK version (1.8). Both computers have Windows (I don't know if they're different, but it should matter).
Any help will be really appreciated.

AES Decryption in Java Not Returning the Same String as the Plaintext

I am trying to create a program in java in which part of it uses AES encryption to encrypt data for my final project in a coding class. Here is the code that I am using for my encryption:
static String symmetric(String info, String key, String mode) {
try {
Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding");
byte [] bytes = Base64.getDecoder().decode(Crypto.sha256(key));
byte [] information = Base64.getDecoder().decode(info);
Key k = new SecretKeySpec(bytes, "AES");
if (mode.equals("ENCRYPT")) {
c.init(Cipher.ENCRYPT_MODE, k);
} else if (mode.equals("DECRYPT")) {
c.init(Cipher.DECRYPT_MODE, k);
}
return (Base64.getEncoder().encodeToString(c.doFinal(information)).trim());
} catch (Exception e) {
JOptionPane.showMessageDialog(null, e.getMessage());
}
return (null);
}
When I encrypt my data using String ciphterText = symmetric("message", "key", "ENCRYPT") and decrypt the ciphertext using symmetric(cipherText, "key", "DECRYPT"), the string it returns is "messagc=". I'm worried that the padding is weird but I don't know how to fix it.
FYI: Crypto.sha256(String input) is a method I created that returns the sha256 hash of info as a base 64 string. Here is the code for it if it helps:
public static String sha256(String input) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte [] tempHash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
return (Base64.getEncoder().encodeToString(tempHash));
} catch (NoSuchAlgorithmException e) {
JOptionPane.showMessageDialog(null, e.getMessage());
}
return (null);
}
Also I know ECB is not secure compared to other methods that use initialization vectors, but it is a small project and I don't have enough time to do that, which is the same reason why I'm not salting my hashes. Is there anything I can do to fix it?
This is a problem with the way you are using base-64 encoding.
When you encrypt, you are treating "message" as base-64 encoded bytes. The last block is "age". A strict decoder would reject that input, because it is missing padding, and has some extra bits that spill over into the third byte. But a permissive decoder ignores that, and decodes the array as { 0x99, 0xeb, 0x2c, 0x6a, 0x07 }
The correct base-64 encoding of { 0x99, 0xeb, 0x2c, 0x6a, 0x07 } is "messagc=".
To make this work correctly, every statement in your method should differ depending on the mode flag. It would be more clear and clean to separate encrypt and decrypt methods.

performance of AES in java: how (if?) to improve

i am having issues with java security. i have a bunch of bytes, and i want to split them into chunks of N bytes, encrypt them, and send them in separate (independant) messages.
here is my basic crypto stuff:
private byte[] generateIv(int size) {
byte[] iv = new byte[size];
randomSecureRandom.nextBytes(iv);
return iv;
}
#Override
public byte[] encryptData(byte[] iv, byte[] in, Key key) throws CryptoException {
try {
Cipher c = Cipher.getInstance("AES/CTR/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
return c.doFinal(in);
} catch (Exception ex) {
throw new CryptoException(ex);
}
}
#Override
public byte[] decryptData(byte[] iv, byte[] in, Key key) throws CryptoException {
try {
Cipher c = Cipher.getInstance("AES/CTR/PKCS5Padding");
c.init(Cipher.DECRYPT_MODE,key, new IvParameterSpec(iv));
return c.doFinal(in);
} catch(Exception ex) {
throw new CryptoException(ex);
}
}
#Override
public byte[] createHMAC(byte[] pauload, Key sigKey) throws CryptoException {
try {
Mac mac = Mac.getInstance("HMACSHA256");
mac.init(sigKey);
byte[] digest = mac.doFinal(pauload);
return digest;
} catch (Exception e) {
throw new CryptoException("unable to create HMAC",e);
}
}
i create the message like this (an AES-128 session key is generated in an initial handshake):
byte[] iv = generateIv();
byte[] hmac = createHMAC (inputBytes,key);
byte[] enc = encryptData(iv,inputBytes,key);
then i write in the message:
[iv bytes][enc bytes][hmac bytes]
on the opposite side, i do:
byte[] iv = readFirstEightBytes(inputBytes);
byte[] dec = decryptData(iv,inputBytes[8..N+8],key);
byte[] hmac = createHMAC(dec,inputBytes[N+9..end],key);
// compare hmac
return dec;
this works all nice and fluffy, except when i start to hammer it, it kind of tends to be really, really, slow. and my hotspots are:
encryptData 27%
createHMAC 22%
decryptData 18%
i was wondering if i'm being naive here? am i missing something obvious? i was wondering if i should maybe not get a fresh Cipher instance every time ... but i got a lot of concurrency, i am guessing that trying to make sharing an instance threadsafe is probably also not ideal ...
i see lots of room for improvement in the rest of my code, so this would make the encryption part look even worse.
who's got thoughts? or is 20 mb/s total throughput as good as it gets (on a modern, beefy laptop)?

Simplest way to encrypt a text file in java

For my School project I had to show that I can utilize file handling within a program. For this I made a very simple login process that you can create an account on that writes a username and password to a text file located in the resource folder. Obviously this has no security at all as it wasn't designed to be secure just to showcase file handling however my teacher has said that I should attempt to add some encryption to the file as well to get a better grade.
I have done some research and many people are recommending DES.
The problem I'm having is I don't have much time left for my project and need to finish it asap. Using DES seems like it would take a while to implement all the extra code.
In my program I am using a simple lineNumberReader to read the files line by line. To write to the files I am using a BufferedWriter.
Is there anyway to encrypt this data very simply? It doesn't have to be very secure but I need to show that I have atleast attempted to encrypt the data. The encryption and decryption would all be completed on the same application as data isn't being transferred.
Potentially a way I can create a very simple encryption and decryption algorithm myself?
Try this,... Its pretty simple
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
public class HelloWorld{
public static void main(String[] args) {
try{
KeyGenerator keygenerator = KeyGenerator.getInstance("DES");
SecretKey myDesKey = keygenerator.generateKey();
Cipher desCipher;
desCipher = Cipher.getInstance("DES");
byte[] text = "No body can see me.".getBytes("UTF8");
desCipher.init(Cipher.ENCRYPT_MODE, myDesKey);
byte[] textEncrypted = desCipher.doFinal(text);
String s = new String(textEncrypted);
System.out.println(s);
desCipher.init(Cipher.DECRYPT_MODE, myDesKey);
byte[] textDecrypted = desCipher.doFinal(textEncrypted);
s = new String(textDecrypted);
System.out.println(s);
}catch(Exception e)
{
System.out.println("Exception");
}
}
}
So basically before writing to file you will encrypt and after reading you will need to decrypt it.
An easy and fun scrambling algorithm would be the Burrows-Wheeler transform. Not really a secure encryption, but seriously, it's a school work and this is awesome.
use simple subtitute encryption algorythm, change every character into number or other character.
get every character of your string.
get the ascii value of the string.
add the ascii value with specific integer (this will be your encryption key)
display the result
A very basic method would be to xor the data with a key. This method is symmetrical, i.e you can use the same key to decode as encode.
If we choose a 1 byte key it's nice and simple, enough to make it unreadable (but not at all secure!):
private void encodeDecode(byte[] bytes, byte key) {
for(int i=0; i<bytes.length; i++)
bytes[i] = (byte) (bytes[i]^key);
}
You could use a simple ceasar cipher (http://en.wikipedia.org/wiki/Caesar_cipher)
public class Cipher {
public static void main(String[] args) {
String str = "The quick brown fox Jumped over the lazy Dog";
System.out.println( Cipher.encode( str, 12 ));
System.out.println( Cipher.decode( Cipher.encode( str, 12), 12 ));
}
public static String decode(String enc, int offset) {
return encode(enc, 26-offset);
}
public static String encode(String enc, int offset) {
offset = offset % 26 + 26;
StringBuilder encoded = new StringBuilder();
for (char i : enc.toCharArray()) {
if (Character.isLetter(i)) {
if (Character.isUpperCase(i)) {
encoded.append((char) ('A' + (i - 'A' + offset) % 26 ));
} else {
encoded.append((char) ('a' + (i - 'a' + offset) % 26 ));
}
} else {
encoded.append(i);
}
}
return encoded.toString();
}
}
Found at http://rosettacode.org/wiki/Caesar_cipher#Java
Note that Java has native solutions for encryption and when it comes to passwords, it is much better to just hash them and compare hashes as there usually is no need to decrypt them.
I don't know who recommends DES to encrypt password.
I suggest you to follow these step if you would to impress your teacher:
cite your reference as theoric support to your cryptographic solution. I sugget you this OWSAP - Password Storage Cheat Sheet
explain where your code meets specification. For a good tutorial with sample code I suggest you this secure password hash
This solution makes your project real and you can reuse it to pass the exam of your future Crypto Module :) . Otherwise I like the solution proposed from StanislavL.
Enjoy!
Bouncy Castle Crypto API is a lightweight cryptography API in Java.
import org.bouncycastle.crypto.*;
import org.bouncycastle.crypto.engines.*;
import org.bouncycastle.crypto.modes.*;
import org.bouncycastle.crypto.params.*;
// A simple example that uses the Bouncy Castle
// lightweight cryptography API to perform DES
// encryption of arbitrary data.
public class Encryptor {
private BufferedBlockCipher cipher;
private KeyParameter key;
// Initialize the cryptographic engine.
// The key array should be at least 8 bytes long.
public Encryptor( byte[] key ){
/*
cipher = new PaddedBlockCipher(
new CBCBlockCipher(new DESEngine()));
*/
cipher = new PaddedBlockCipher(
new CBCBlockCipher(new BlowfishEngine()));
this.key = new KeyParameter( key );
}
// Initialize the cryptographic engine.
// The string should be at least 8 chars long.
public Encryptor( String key ){
this( key.getBytes());
}
// Private routine that does the gritty work.
private byte[] callCipher( byte[] data )
throws CryptoException {
int size = cipher.getOutputSize( data.length );
byte[] result = new byte[ size ];
int olen = cipher.processBytes(data,0,data.length result, 0);
olen += cipher.doFinal( result, olen );
if( olen < size ){
byte[] tmp = new byte[ olen ];
System.arraycopy(
result, 0, tmp, 0, olen );
result = tmp;
}
return result;
}
// Encrypt arbitrary byte array, returning the
// encrypted data in a different byte array.
public synchronized byte[] encrypt( byte[] data )
throws CryptoException {
if( data == null || data.length == 0 ){
return new byte[0];
}
cipher.init( true, key );
return callCipher( data );
}
// Encrypts a string.
public byte[] encryptString( String data )
throws CryptoException {
if( data == null || data.length() == 0 ){
return new byte[0];
}
return encrypt( data.getBytes() );
}
// Decrypts arbitrary data.
public synchronized byte[] decrypt( byte[] data )
throws CryptoException {
if( data == null || data.length == 0 ){
return new byte[0];
}
cipher.init( false, key );
return callCipher( data );
}
// Decrypts a string that was previously encoded
// using encryptString.
public String decryptString( byte[] data )
throws CryptoException {
if( data == null || data.length == 0 ){
return "";
}
return new String( decrypt( data ) );
}
}
There are too many ways to encrypted simple string in Java. If it is a school project , I really don't think you can get a higher band by simply using some third-part libs to finish the encrypted work.
If you have some time, you could try to understand how Base64 works, then try to create some encrypted algorithm by yourself.
How ever, if you insist to use some API in Java , I have to say that DES is really old way to encrypted text , 3DEs(DESede) or AES will be better and safer , both of them have already been supported since Java6.
If you have to import the BouncyCastle lib , I prefer IDEA, it's one of the safest algorithm, may have you achieve a good score.
I won't give you any demo code, but you can easily find some by google all the algorithm I have mentioned.
public class CryptoUtils {
public static void encrypt(String key, File inputFile, File outputFile)
throws CryptoException {
doCrypto(Cipher.ENCRYPT_MODE, key, inputFile, outputFile);
}
public static void decrypt(String key, File inputFile, File outputFile)
throws CryptoException {
doCrypto(Cipher.DECRYPT_MODE, key, inputFile, outputFile);
}
private static void doCrypto(int cipherMode, String key, File inputFile,
File outputFile) throws CryptoException {
try {
Key secretKey = new SecretKeySpec(key.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(cipherMode, secretKey);
FileInputStream inputStream = new FileInputStream(inputFile);
byte[] inputBytes = new byte[(int) inputFile.length()];
inputStream.read(inputBytes);
byte[] outputBytes = cipher.doFinal(inputBytes);
FileOutputStream outputStream = new FileOutputStream(outputFile);
outputStream.write(outputBytes);
inputStream.close();
outputStream.close();
} catch (NoSuchPaddingException | NoSuchAlgorithmException
| InvalidKeyException | BadPaddingException
| IllegalBlockSizeException | IOException ex) {
throw new CryptoException("Error encrypting/decrypting file", ex);
}
}
}
package net.codejava.crypto;
import java.io.File;
public class CryptoException extends Exception {
public CryptoException() {
}
public CryptoException(String message, Throwable throwable) {
super(message, throwable);
}
public static void main(String[] args) {
String key = "Mary has one cat1";
File inputFile = new File("document.txt");
File encryptedFile = new File("document.encrypted");
File decryptedFile = new File("document.decrypted");
try {
CryptoUtils.encrypt(key, inputFile, encryptedFile);
CryptoUtils.decrypt(key, encryptedFile, decryptedFile);
} catch (CryptoException ex) {
System.out.println(ex.getMessage());
ex.printStackTrace();
}
}
}
My suggestion: don't use encryption at all.
Here's something better:(I hope)
Scanner sc=new Scanner(System.in);
String name=sc.next();
//for inputting user name
File f= new File("d://"+name+".txt");
if(f.exists())
{
if(f.lastModified()!=0)
{
System.out.println("Account data tampered...cannot be accessed");
}
else{
String data="";
System.out.println(data); //data should contain
//data from file read using BufferedReader
f.setLastModified(0);
}
}
else
{
f.createNewFile();//Write whatever you want to to the file
f.setLastModified(0);
}
So, you can effectively know whether the user has tampered with the text file with the details and display an error message if the tampered account is used.
However, This does not prevent the user from changing the file, it will just prevent a tampered account from being used....I think your computer teacher might like this.
You could also do:
f.setReadOnly();
and when you write to the file,
f.setWritable(true,true),
then after closing the output stream,
f.setReadOnly();
again...
But the file can still be replaced, therefore the 1st and is more
Effective.
Thanks
you can use these function to encrypt and decrypt simple text
//Encrypt simple text
public String EncryptSimpleText (String text2Encrypt) throws Exception {
byte[] encryptArray = Base64.getEncoder().encode(text2Encrypt.getBytes());
return new String(encryptArray,"UTF-8");
}
//Decrypt simple text
public String Decrypt2SimpleText(String textEncrypted) throws Exception {
byte[] dectryptArray = textEncrypted.getBytes();
byte[] decarray = Base64.getDecoder().decode(dectryptArray);
return new String(decarray,"UTF-8");
}

AES-128 Encryption not working on Java < 1.7

I've been chipping away at a school assignment for 3 days, and finally finished it today, error-free and working fine! Except, I was testing it on Java 1.7, and the school servers (where the professor will compile it) run 1.6. So, I tested my code on 1.6, wanting to cover all my bases, and I get a BadPaddingException upon decryption.
[EDIT] Warning: this code does not follow common security practices and should not be used in production code.
Originally, I had this, which works fine on 1.7 (sorry, lots of code.. all relevant..):
public static String aes128(String key, String data, final int direction) {
SecureRandom rand = new SecureRandom(key.getBytes());
byte[] randBytes = new byte[16];
rand.nextBytes(randBytes);
SecretKey encKey = new SecretKeySpec(randBytes, "AES");
Cipher cipher = null;
try {
cipher = Cipher.getInstance("AES");
cipher.init((direction == ENCRYPT ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), encKey);
} catch (InvalidKeyException e) {
return null;
} catch (NoSuchPaddingException e) {
return null;
} catch (NoSuchAlgorithmException e) {
return null;
}
try {
if (direction == ENCRYPT) {
byte[] encVal = cipher.doFinal(data.getBytes());
String encryptedValue = Base64.encode(encVal);
return encryptedValue;
} else {
byte[] dataBytes = Base64.decode(data);
byte[] encVal = cipher.doFinal(dataBytes);
return new String(encVal);
}
} catch (NullPointerException e) {
return null;
} catch (BadPaddingException e) {
return null;
} catch (IllegalBlockSizeException e) {
return null;
}
}
However, my BadPaddingException catch block executes upon decryption:
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at CipherUtils.aes128(CipherUtils.java:112)
at CipherUtils.decryptFile(CipherUtils.java:44)
at decryptFile.main(decryptFile.java:21)
This is what I tried to fix it (basically, I added all the padding/unpadding myself, and used NoPadding):
public static String aes128(String key, String data, final int direction) {
// PADCHAR = (char)0x10 as String
while (key.length() % 16 > 0)
key = key + PADCHAR; // Added this loop
SecureRandom rand = new SecureRandom(key.getBytes());
byte[] randBytes = new byte[16];
rand.nextBytes(randBytes);
SecretKey encKey = new SecretKeySpec(randBytes, "AES");
AlgorithmParameterSpec paramSpec = new IvParameterSpec(key.getBytes()); // Created this
Cipher cipher = null;
try {
cipher = Cipher.getInstance("AES/CBC/NoPadding"); // Added CBC/NoPadding
cipher.init((direction == ENCRYPT ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), encKey, paramSpec); // Added paramSpec
} catch (InvalidKeyException e) {
return null;
} catch (NoSuchPaddingException e) {
return null;
} catch (NoSuchAlgorithmException e) {
return null;
} catch (InvalidAlgorithmParameterException e) {
return null; // Added this catch{}
}
try {
if (direction == ENCRYPT) {
while (data.length() % 16 > 0)
data = data + PADCHAR; // Added this loop
byte[] encVal = cipher.doFinal(data.getBytes());
String encryptedValue = Base64.encode(encVal);
return encryptedValue;
} else {
byte[] dataBytes = Base64.decode(data);
byte[] encVal = cipher.doFinal(dataBytes);
return new String(encVal);
}
} catch (NullPointerException e) {
return null;
} catch (BadPaddingException e) {
return null;
} catch (IllegalBlockSizeException e) {
return null;
}
}
When using this, I just get gibberish in and out:
Out: u¢;èÉ÷JRLòB±J°N°[9cRÐ{ªv=]I¯¿©:
´RLA©êí;R([¶Ü9¸ßv&%®µ^#û|Bá (80)
Unpadded: u¢;èÉ÷JRLòB±J°N°[9cRÐ{ªv=]I¯¿©:
´RLA©êí;R([¶Ü9¸ßv&%®µ^#û|Bá (79)
It is also worth noting that 1.6 and 1.7 produce different encrypted strings.
For example, on 1.7, encrypting xy (including a SHA-1 hash) with key hi produces:
XLUVZBIJv1n/FV2MzaBK3FLPQRCQF2FY+ghyajdqCGsggAN4aac8bfwscrLaQT7BMHJgfnjJLn+/rwGv0UEW+dbRIMQkNAwkGeSjda3aEpk=
On 1.6, the same thing produces:
nqeahRnA0IuRn7HXUD1JnkhWB5uq/Ng+srUBYE3ycGHDC1QB6Xo7cPU6aEJxH7NKqe3kRN3rT/Ctl/OrhqVkyDDThbkY8LLP39ocC3oP/JE=
I didn't expect the assignment to take so long, so my time has run out and it does need to be done tonight. If there is no answer by then, however, I'll just leave a note to my teacher regarding this. It appears to be some issue that was fixed in 1.7... though hopefully can be remedied through the correct addition/fix in my code.
Thanks a ton for everyone's time!
First off:
For almost all systems, encrypting the same plaintext twice should always (i.e. with very very high probability) produce different ciphertext.
The traditional example is that it allows a CPA adversary to distinguish E("attack at dawn") from E("attack at dusk") with only two queries. (There are a handful of systems where you want deterministic encryption, but the right way to do this is "synthetic IV" or cipher modes like CMC and EME.)
Ultimately, the problem is that SecureRandom() is not intended for key derivation.
If the input "key" is a passphrase, you should be using something like PBKDF2 (or scrypt() or bcrypt()).
Additionally, you should be using an explicit charset, e.g. String.getBytes("UTF-8").
If the input "key" is a key, the most common string representation is a hexdump. Java doesn't include an unhexing function, but there are several here.
If the input is a "master key" and you want to derive a subkey, then you should be hashing it with other data. There's not much point if the subkey is always the same.
Additional nitpicks:
Your code is vulnerable to a padding oracle attack; you really should be verifying a MAC before doing anything with the data (or better, using an authenticated encryption mode).
In your second listing, you explicitly reuse the IV. Bad! Assuming CBC mode, the IV used should be unpredictable; SecureRandom is useful here.
I've been looking over and over and I have to agree with NullUserException. The problem is the use of SecureRandom. This means that you never really know what your key is and therefore it is not necessarily ever the same key.
encKey comes from SecureRandom, which is seeded by the key provided. Therefore, if the key is the same, the seed is the same, so the random should be the same...
...unless of course Oracle (or another provider) changes the implementation between versions.
Okay, adding more information that I researched. I think this answer was most helpful.
Get password and cleartext from the user, and convert them to byte arrays.
Generate a secure random salt.
Append the salt to the password and compute its cryptographic hash. Repeat this many times.
Encrypt the cleartext using the resulting hash as the initialization vector and/or secret key.
Save the salt and the resulting ciphertext.
To me, it sounds like SecureRandom is used once to generate a salt but then salt must be saved with the cypher text in order to undo the cyphering process. Additional security comes from repetition and variance of steps (obscurity).
Note: I couldn't find any consensus that these steps are best practices.

Categories