How to saved encrypted/hashing password in XML file using Java - java

I want to save the password in XML file. But the plaintext is not secure enough. So I want to save it in hashed value or encrypted data. But I don't know how to do this. Since MD5 or SHA-2 is one way hashing. Or if I use salt, it should save the value of salt.
So what can I do? Please help me with this problem.

You can use javax.crypto package to encrypt/decrypt password.
First of all you have to define encryption secret and encryption init vector. For instance:
String secret = "Foo12345Bar12345";
String initVector = "randomInitVector";
Then you can write methods to encrypt/decrypt password.
public static String encrypt(String value, String secret, String initVector) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes());
SecretKeySpec keySpec = new SecretKeySpec(secret.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
byte[] encrypted = cipher.doFinal(value.getBytes());
return Base64.encodeBase64String(encrypted);
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
public static String decrypt(String value, String secret, String initVector) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes());
SecretKeySpec keySpec = new SecretKeySpec(secret.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
byte[] decrypted = cipher.doFinal(Base64.decodeBase64(str));
return new String(decrypted);
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
To run this code you need to add apache dependency to your project
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.9</version>
</dependency>
In addition, Java 8 has already tools to decode/encode Base64:
java.util.Base64.getDecoder() and java.util.Base64.getEncoder() so you can replace Apache with Java 8 impl

Related

Lightweight way to encrypt/decrypt cookies in java

We need to keep some flags in the cookies for a spring MVC application
It will be checked and set in an interceptor for every request. Since we need to make the application stateless we don't want to store anything in the session.
My question is how do we encrypt/decrypt the cookie most efficiently? (As less CPU/time as possible).
Currently with AES encryption it takes around 200ms to encrypt and another similar time to decrypt. This is very high overhead considering we need to do it for every request.
Updated question with AES code taking long time
public static String encrypt(String value) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(value.getBytes());
return Base64.encodeBase64String(encrypted);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static String decrypt(String encrypted) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));
return new String(original);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
Can anyone suggest standard practices for this kind of requirements?
Thanks in advance.

Obtaining Secret Key from cert file to use in AES algorithm for encryption and decryption

I am doing a AES encryption , in which i will use a secret key from cert file as below to initialise the cipher.
encryptModeCipher = Cipher.getInstance("AES");
encryptModeCipher.init(Cipher.ENCRYPT_MODE, aesSecretKey);
But the problem i see here is that, my secretKey () remains the same for all the certificates that i use. Any suuggestion why? and suggest a good idea to do so.
byte[] encryptionKey = Arrays.copyOf(encoded, 32);
secretKey = new SecretKeySpec(encryptionKey, algorithm);
public class AESEncryptionServiceHelper {
private String algorithm = "AES";
private String certPass;
private SecretKey secretKey;
public SecretKey setKey() {
try {
certPass="****";
char[] pass = certPass.toCharArray();
KeyStore keyStore = KeyStore.getInstance("jceks");
File file = new File("D:/aws-kms-dps/***.jks");
InputStream inputStream = new FileInputStream(file);
keyStore.load(inputStream, pass);
Certificate cert = keyStore.getCertificate("****");
Key key = cert.getPublicKey();
secretKey = new SecretKeySpec(key.getEncoded(), algorithm);
byte[] encoded = secretKey.getEncoded();
byte[] encryptionKey = Arrays.copyOf(encoded, 32);
secretKey = new SecretKeySpec(encryptionKey, algorithm);
} catch (IOException e) {
System.out.println(e);
} catch (Exception e) {
System.out.println(e);
}
return secretKey;
}
public static void main(String args[]){
AESEncryptionServiceHelper aesEncryptionServiceHelper=new AESEncryptionServiceHelper();
aesEncryptionServiceHelper.setKey();
}
}
You seems you are usging (part of) the public key as an AES key. That is VERY BAD idea as
the public key is .. well .. public and static
it has relatively low entropy (as multiple bytes are defined in the ASN.1 format)
Did you do any research how to properly do encryption using PKI or you are just guessing / plaing with the crypto API?
Let's assume you want to do encryption using the public key and AES (it is called hybrid encryption), you could take example from my blog
Please read it and understand (or any other good blogs about cryptography), seems you are missing using IV (salt) and MAC
// generate random AES key
KeyGenerator keyGenerator = KeyGenerator.getInstance(SYMMETRIC_KEY_ALG);
SecretKey symmetricKey = keyGenerator.generateKey();
// this assumes there's whole keypair (including private key)
// normally only a certificate with PubKey is available
PublicKey pubKey = keystoreEntry.getCertificate().getPublicKey();
params.setKey(symmetricKey.getEncoded());
// execute symmetric encryption
this.symmetricEncryption(params);
// encrypt the key with the public key
Cipher cipher = Cipher.getInstance(PKI_CIPHER_ALG);
cipher.init(Cipher.WRAP_MODE, pubKey);
byte[] wrappedKey = cipher.wrap(symmetricKey);
LOGGER.log(Level.INFO, "Wrapped key: {0}", Base64.getEncoder().encodeToString(wrappedKey));
params.setKey(wrappedKey);
where the symetric encryption itself can be implemented as follows
// initialization vector
SecureRandom rnd = new SecureRandom();
byte[] iv = new byte[SYMMETRIC_BLOCK_SIZE / 8];
rnd.nextBytes(iv);
encryptionParams.setIv(iv);
IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
SecretKey symmetricKey = new SecretKeySpec(encryptionParams.getKey(), SYMMETRIC_KEY_ALG);
Cipher cipher = Cipher.getInstance(SYMMETRIC_CIPHER_NAME);
cipher.init(Cipher.ENCRYPT_MODE, symmetricKey, ivParamSpec);
// for HMAC we should be able to use the same key as for encryption
// for CBC-MAC it may not be the case
// https://en.wikipedia.org/wiki/CBC-MAC#Using_the_same_key_for_encryption_and_authentication
Mac mac = Mac.getInstance(EncryptionTest.HASH_ALGORITHM_NAME);
mac.init(symmetricKey);
byte[] encrypted = cipher.doFinal(encryptionParams.getPlaintext());
encryptionParams.setCiphertext(encrypted);
byte[] authTag = mac.doFinal(encrypted);
encryptionParams.setMac(authTag);

BadPaddingException when decrypting using a different Cipher object

Please excuse the hackjob! Still new at coding and was utilizing a lot of sys outs to troubleshoot, and this is my first post at StackOverflow. Thank you for the help!
So I've been working on trying to encrypt String objects using the javax.crypto.Cipher API, and I have found some success, but only when it is using the same instance of the Cipher object. However, for the purposes of my project, I am encrypting text (String) to and decrypting text from a text file, and will not be accessing the same Cipher object every time.
I believe the issue is not from converting between the byte arrays and Strings, since the Base64 encoder seems to have taken care of that problem. The output of the byte arrays are identical pre-encoding and post-decoding, so that should isolate it as an issue during the decryption phase. What can be done so that my decryptPW method can use a different Cipher instance (with the same arguments passed) and not trigger a BadPaddingException?
private static String encryptPW(String pw){
byte[] pwBytes = pw.getBytes();
byte[] keyBytes = "0123456789abcdef".getBytes();
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
try {
Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
ciph.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] encryptedBytes = ciph.doFinal(pwBytes);
pw = Base64.getEncoder().encodeToString(ciph.doFinal(pwBytes));
for (byte b : encryptedBytes){
System.out.print(b);
}
System.out.println();
} catch (Exception e){
e.printStackTrace();
}
return pw;
}
private static String decryptPW(String pw){
byte[] pwBytes = Base64.getDecoder().decode(pw.getBytes());
for (byte b : pwBytes){
System.out.print(b);
}
System.out.println();
byte[] keyBytes = "0123456789abcdef".getBytes();
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
try {
Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
ciph.init(Cipher.DECRYPT_MODE, keySpec, ciph.getParameters());
pw = new String(ciph.doFinal(pwBytes));
} catch (Exception e){
e.printStackTrace();
}
return pw;
}
Again, thank you!
As you use CBC mode, you need to save the random Initialization Vector (IV) from the encryption cipher and provide it to the decryption cipher.
You can get it from the encryption cipher after init like:
ciph.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] iv = ciph.getIV();
And provide it to the decrypt cipher with something like:
ciph.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(iv));
You need to come up with a method of saving the IV. A common way is to prefix the encrypted data with the IV, so it's available when you need to initialize your decrypt cipher. It don't need to be secret.

JAVA: AES Decryption

I'm currently running into problems decrypting my data. The base64 of the encoded string is being stored in the database. So, I'm printing out the encoded string and then trying to run it back through with "DECRYPT" instead of "ENCRYPT". However, I never get a value that the Decrypter method likes, it always gives me an error about parameters or the value not being 16 bytes.
public class crypto {
public static void main(String [] args) {
String s = args[0];
String s1 = args[1];
String ivkey = "thisisasecretkey";
byte[] ivraw = ivkey.getBytes();
SecretKeySpec skeySpec = new SecretKeySpec(ivraw, "AES");
if (s.equalsIgnoreCase("ENCRYPT")) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(s1.getBytes());
System.out.println(new String(Base64.encodeBase64(encrypted)));
} catch (Exception e) {
throw new RuntimeException(e);
}
} else {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(s1.getBytes());
System.out.println(new String(Base64.decodeBase64(encrypted)));
} catch (Exception e) {
e.printStackTrace();
}
}
return;
};
}
command:crypto "ENCRYPT" "password"
output: 5eQvSzPG1TE2AybgCmeV6A==
command:crytpo "DECRYPT" "5eQvSzPG1TE2AybgCmeV6A=="
output: java.security.InvalidKeyException: Parameters missing
I'm aware of the security flaws, that's not what I'm asking about and I would prefer answers/comments not get cluttered with best practices.
You should do base 64 decoding, and you should do that before decrypting.
You are not including the initialization vector (IV).
AES in CBC mode has both a 16 byte IV and the 16 byte symmetric key.
String IV = "AAAAAAAAAAAAAAAA"; // generate this randomly
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(IV.getBytes()));
byte[] encrypted = cipher.doFinal(s.getBytes());
Edit: as it turns out, encryption does not require a IV to be provided (as owlstead pointed out), but decryption does. The best bet would be to be explicit and use IV in both encryption and decryption. Change your decryption function to include the IV, and you will run into the other error in your code that owlstead pointed out.

AES 128 encryption in Android and .Net with custom key and IV

I have a password string in my android application. I need to the send the password through the .net web service (i.e. end with .aspx) using the SOAP web service. Before sending the password i need to encrypt the password with AES 128 encryption with the custom key and IV.
They have a encrypt/decrypt tool in .net with the custom key and Iv. The tool ask a custom key with 16 digit and IV 8 digit. If give the string it generate the encrypting string. example
Example:
Key : 1234567812345678
IV : 12345678
String : android
Encrypted string : oZu5E7GgZ83Z3yoK4y8Utg==
I didn't have any idea how to do this in android. Need help.
A complete example may help you:
The encrypt/decrypt functions, using IV
public static byte[] encrypt(byte[] data, byte[] key, byte[] ivs) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
byte[] finalIvs = new byte[16];
int len = ivs.length > 16 ? 16 : ivs.length;
System.arraycopy(ivs, 0, finalIvs, 0, len);
IvParameterSpec ivps = new IvParameterSpec(finalIvs);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivps);
return cipher.doFinal(data);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static byte[] decrypt(byte[] data, byte[] key, byte[] ivs) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
byte[] finalIvs = new byte[16];
int len = ivs.length > 16 ? 16 : ivs.length;
System.arraycopy(ivs, 0, finalIvs, 0, len);
IvParameterSpec ivps = new IvParameterSpec(finalIvs);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivps);
return cipher.doFinal(data);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
You can use it as below :
String dataToEncryptDecrypt = "android";
String encryptionDecryptionKey = "1234567812345678";
String ivs = "12345678";
byte[] encryptedData = encrypt(dataToEncryptDecrypt.getBytes(), encryptionDecryptionKey.getBytes(),
ivs.getBytes());
// here you will get the encrypted bytes. Now you can use Base64 encoding on these bytes, before sending to your web-service
byte[] decryptedData = decrypt(encryptedData, encryptionDecryptionKey.getBytes(), ivs.getBytes());
System.out.println(new String(decryptedData));
I don't know the details of AES algorithm in use(ie mode & padding method), bit it should be roughly like this:
public static byte[] encrypt(byte[] data, byte[] key) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/ZeroBytePadding");
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
byte[] empty = new byte[16]; // For better security you should use a random 16 byte key!!!
IvParameterSpec ivps = new IvParameterSpec(empty);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivps);
return cipher.doFinal(data);
} catch (Exception e) {
// ...
}
return null;
}
Function above could be used like this:
String data = "android";
String key = "1234567812345678";
byte encrypted = encrypt(data.getbytes("UTF-8"), key.getbytes("UTF-8"));

Categories

Resources