protected static byte[] getHashedKey(String password,String MODE)throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
saltIV = new byte[48];
salt = new byte[32];
ivBytes =new byte[16];
if(MODE.equals("CREATE_VAULT")){
//generate salt ,iv & save them
salt = generateSalt();
ivBytes = generateIV();
System.arraycopy(salt, 0, saltIV, 0, salt.length);
System.arraycopy(ivBytes, 0, saltIV, salt.length, ivBytes.length);
//save salt & iv
//FileOutputStream saltIvOutFile = new FileOutputStream("C:\\saltIv.ats");
//saltIvOutFile.write(saltIV);
//saltIvOutFile.close();
}
if(MODE.equals("OPEN_VAULT")){
FileInputStream saltIvInFile = new FileInputStream("C:\\saltIv.ats");
saltIvInFile.read(saltIV);
saltIvInFile.close();
System.arraycopy(saltIV, 0, salt, 0, salt.length);
System.arraycopy(saltIV, salt.length, ivBytes, 0, ivBytes.length);
}
// Derive the key
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
PBEKeySpec spec = new PBEKeySpec(
password.toCharArray(),
salt,
pswdIterations,
keySize
);
SecretKey secretKey = factory.generateSecret(spec);
return secretKey.getEncoded();
}
this is where the hashed password gets generated and
try {
String finalKey = Arrays.toString(androCrypter.getHashedKey(v.password,"CREATE_VAULT"));
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
boolean vaultCreationSuccessful = new TableController(context).addNewVault(finalKey,v.vaultName,v.algorithm);
this is where I save the hash as string and insert it to database
but it never gets saved.The sqlite column is TEXT type.I came to know that sqlite supports directly saving byte array so I tried with byte[] also but same result,no entry in database.There is nothing wrong with addVault() code as I have checked it with other normal string and all,it works just fine.
Here is the code
protected boolean addNewVault(String finalKey,String vaultName,String algorithm) {
ContentValues values = new ContentValues();
values.put("Vault_Name", vaultName);
values.put("KEY", finalKey);
values.put("Algorithm", algorithm);
SQLiteDatabase db = this.getWritableDatabase();
boolean createSuccessful = db.insert(DATABASE_NAME, null, values) > 0;
db.close();
return createSuccessful;
}
But in this case I cant get it to work.Nothing gets saved.I also read about BLOB but I think its not needed as because byte[] is supported in sqlite.
What is wrong ?
Any help/suggestion would be really appreciated.Thank you.
NOTE:Please ignore the FileInputStream and FileOutputtream lines in getHashedKey();
EDIT:
I just figured out that this
try {
finalKey = Arrays.toString(androCrypter.getHashedKey(v.password,"CREATE_VAULT"));
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
code is not getting executed that is the problem. I have created another class just to check if it works there and it does.
Tried another method and calling it like finalKey= getFinalKey(v.password) and this method contains the above code but that does not work.
I am completely out of clue.Please help.
What I would do is to convert the "byte[]" to String:
String hashAsString = new String(hashAsBytes,"UTF-8");
Where "hashAsBytes" is the the resulting of "getHashedKey", and "hashAsString" is your byte array converted to a String.
Then you just save the "hashAsString" in your DB, as you see fit.
Then to compare hashes:
public boolean compareHashes(String hashInDB, String hashNew){
return hashInDB.equals(hashNew);
}
This method will return TRUE if your hashInDB is equal to the new hash you have just calculated. In case the hashes do not match it will return FALSE.
Hope this helps!
Best, Federico.
Related
I have this code here which I'm having difficulty converting to VB.NET and hoping someone who give me a hand with it.
I'm having difficulty with the part on the cipher - I have tried Googling but was unable to find a clear resource explaining it in simple terms. (I don't know how to proceed with it as it seems there's no equivalent for it??)
I have almost no knowledge of java hence am trying to google and find out what each part means and convert it after. Hope someone would be able to point me in the right direction!
public static byte[] decryptPDF(String password, String filePath) {
try {
byte[] headerSaltAndCipherText = Base64.decode(IOUtils.toString(new FileInputStream(new File(filePath)), "UTF-8").toString().getBytes("UTF-8"), 0);
byte[] salt = Arrays.copyOfRange(headerSaltAndCipherText, 8, 16);
byte[] encrypted = Arrays.copyOfRange(headerSaltAndCipherText, 16, headerSaltAndCipherText.length);
Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[][] keyAndIV = EVP_BytesToKey(32, aesCBC.getBlockSize(), MessageDigest.getInstance(CommonUtils.MD5_INSTANCE), salt, password.getBytes("UTF-8"), 1);
aesCBC.init(2, new SecretKeySpec(keyAndIV[0], "AES"), new IvParameterSpec(keyAndIV[1]));
return aesCBC.doFinal(encrypted);
} catch (IOException e) {
e.printStackTrace();
return null;
} catch (NoSuchPaddingException e2) {
e2.printStackTrace();
return null;
} catch (InvalidAlgorithmParameterException e3) {
e3.printStackTrace();
return null;
} catch (NoSuchAlgorithmException e4) {
e4.printStackTrace();
return null;
} catch (IllegalBlockSizeException e5) {
e5.printStackTrace();
return null;
} catch (BadPaddingException e6) {
e6.printStackTrace();
return null;
} catch (InvalidKeyException e7) {
e7.printStackTrace();
return null;
}
}
What I've tried so far is...
Public Shared Function decryptPDF(ByVal password As String, ByVal fileAsString As String) As Byte()
Try
Dim headerSaltAndCipherText() As Byte = System.Convert.FromBase64String(fileAsString)
Dim salt() As Byte = headerSaltAndCipherText.Skip(7).Take(8)
Dim encrypted() As Byte = headerSaltAndCipherText.Skip(15).Take(headerSaltAndCipherText.Length)
Dim aesCBC As Cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
Dim keyAndIV() As Byte = EVP_BytesToKey(32, aesCBC.getBlockSize, MessageDigest.getInstance(CommonUtils.MD5_INSTANCE), salt, password.getBytes("UTF-8"), 1)
aesCBC.init(2, New SecretKeySpec(keyAndIV(0), "AES"), New IvParameterSpec(keyAndIV(1)))
Return aesCBC.doFinal(encrypted)
End Try
End Function
Try that
Public Shared Function decryptPDF(ByVal password As String, ByVal filePath As String) As Byte()
Try
Dim headerSaltAndCipherText() As Byte = Base64.decode(IOUtils.toString(New FileInputStream(New File(filePath)), "UTF-8").toString.getBytes("UTF-8"), 0)
Dim salt() As Byte = Arrays.copyOfRange(headerSaltAndCipherText, 8, 16)
Dim encrypted() As Byte = Arrays.copyOfRange(headerSaltAndCipherText, 16, headerSaltAndCipherText.length)
Dim aesCBC As Cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
Dim keyAndIV(,) As Byte = EVP_BytesToKey(32, aesCBC.getBlockSize, MessageDigest.getInstance(CommonUtils.MD5_INSTANCE), salt, password.getBytes("UTF-8"), 1)
aesCBC.init(2, New SecretKeySpec(keyAndIV(0), "AES"), New IvParameterSpec(keyAndIV(1)))
Return aesCBC.doFinal(encrypted)
Catch e As IOException
e.printStackTrace
Return Nothing
Catch e2 As NoSuchPaddingException
e2.printStackTrace
Return Nothing
Catch e3 As InvalidAlgorithmParameterException
e3.printStackTrace
Return Nothing
Catch e4 As NoSuchAlgorithmException
e4.printStackTrace
Return Nothing
Catch e5 As IllegalBlockSizeException
e5.printStackTrace
Return Nothing
Catch e6 As BadPaddingException
e6.printStackTrace
Return Nothing
Catch e7 As InvalidKeyException
e7.printStackTrace
Return Nothing
End Try
End Function
I'm trying to encrypt & decrypt password and for these generating key so far so good.Now I need to store this key in properties file but when I add the key it look like this :
#Tue Nov 01 08:22:52 EET 2016
KEY=\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000
So I suspect from my code maybe something wrong ?!?!
And there is a part of my code =
private byte[] key = new byte[16];
public void addProperties(String x, String z) {
Properties properties = new Properties();
String propertiesFileName = "config.properties";
try {
OutputStream out = new FileOutputStream(propertiesFileName);
properties.setProperty(x, z);
properties.store(out, null);
} catch (IOException e) {
e.printStackTrace();
}
}
public void generateKey() {
KeyGenerator keygen;
SecretKey secretKey;
byte[] keybyte = new byte[64];
try {
keygen = KeyGenerator.getInstance("AES");
keygen.init(128);
secretKey = keygen.generateKey();
keybyte = secretKey.getEncoded();
key = keybyte;
//THIS METHOD ADDING PROP TO PROPERTIES FILE
addProperties("KEY", new String(key));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
Thanks for help.All answers acceptable.
KeyGenerator#generateKey() has return type of SecretKey and from javadocs
Keys that implement this interface return the string RAW as their
encoding format (see getFormat), and return the raw key bytes as the
result of a getEncoded method call. (The getFormat and getEncoded
methods are inherited from the java.security.Key parent interface.)
So you need to convert them and there is already asked question on this
String encodedKey = Base64.getEncoder().encodeToString(secretKey.getEncoded());
SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
I am working on decrypting a binary file encrypted in C# using Rijndael encryption method. The file is copied to an android device. The decryption logic works fine when run in a java based desktop test program. But it throws java.io.IOException: last block incomplete when run in android. I am using the code below.
public static void Decrypt(String fileIn, String fileOut, byte[] key, byte[] IV, long offset)
{
// First we are going to open the file streams
FileInputStream fsIn;
try
{
fsIn = new FileInputStream(fileIn);,,
FileOutputStream fsOut = new FileOutputStream(fileOut);
// create cipher object
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(IV));
// create the encryption stream
CipherInputStream cis = new CipherInputStream(fsIn, cipher);
// set a buffer and keep writing to the stream
int bufferLen = KiloByte;
byte[] buffer = new byte[bufferLen];
int bytesRead = 0;
// read a chunk of data from the input file
while ( (bytesRead = cis.read(buffer, 0, bufferLen)) != -1)
{
// write to file
fsOut.write(buffer, 0, bytesRead);
}
fsOut.flush();
// close streams
fsOut.close();
cis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
The key is generated by using the function
public static byte[] GetKey(String password, byte[] IV, int length)
throws NoSuchAlgorithmException, InvalidKeySpecException
{
// Length is kept 16 to make it compatible with all platforms
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec ks = new PBEKeySpec(password.toCharArray(), IV, 1000, length*8);
SecretKey s = f.generateSecret(ks);
Key k = new SecretKeySpec(s.getEncoded(),"AES");
return k.getEncoded();
}
I have gone through many posts on internet related to the topic. Based on that, I have made sure that I use byte array rather String. But still getting this issue.
I'm on Android, so it's just java, I have the same input strings, but getting different values each time. I am missing anything? Thanks
private String getShortenedKey(String key) {
String shortenedKey=null;
MessageDigest md = null;
LogUtils.LOGD(HASH_ALGO, "before key: "+ System.currentTimeMillis());
try {
md = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
shortenedKey = key;
}
LogUtils.LOGD(HASH_ALGO, "after key: "+ System.currentTimeMillis());
md.update(key.getBytes());
byte[] shortenedBytes = md.digest();
shortenedKey = String.valueOf(shortenedBytes);
return shortenedKey;
}
Input string:
{"config":{"wp":"(1.000000,1.000000,1.000000,1.000000)","v":"8","unit":"auto","ef":true,"ws":1,"tt":0,"cs":1},"items":[{"startTime":1409180400,"id":"WorkXYZ#habit.skedgo.com_1409180400","class":"event","endTime":1409209200,"location":{"lng":151.20785,"lat":-33.85926},"priority":0},{"startTime":1409148000,"id":"HomeXYZ#habit.skedgo.com_1409148000","class":"event","endTime":1409234340,"location":{"lng":151.18089,"lat":-33.89153},"priority":0}]}
Update: so many valid answers, thanks. I pick the one that is easiest to change. Cheers.
This line
shortenedKey = String.valueOf(shortenedBytes);
is not doing what you think.
In order to get a string representation of the byte values inside the array you need to implement a little utility method.
Furthermore, if the call to MessageDigest.getInstance("MD5"); ever throws a NoSuchAlgorithmException your program will crash a little later here md.update(key.getBytes()); with a NullPointerException.
As #Henry explained the issue at his answer, String.valueOf(shortenedBytes) must be changed.
Replace this;
shortenedKey = String.valueOf(shortenedBytes);
to this;
shortenedKey = new String(Base64.encode(shortenedBytes))
You can use Base64 from Bouncycastle
Download the jar
check the modified version.
you could use base64 encoding for bytes
private String getShortenedKey(String key) {
String shortenedKey=null;
MessageDigest md = null;
LogUtils.LOGD(HASH_ALGO, "before key: "+ System.currentTimeMillis());
try {
md = MessageDigest.getInstance("MD5");
md.update(key.getBytes());
byte[] shortenedBytes = md.digest();
shortenedKey = Base64.encodeToString(shortenedBytes, Base64.NO_WRAP);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
shortenedKey = key;
}
LogUtils.LOGD(HASH_ALGO, "after key: "+ System.currentTimeMillis());
return shortenedKey;
}
I am first going to describe the problem which I have, and then give some background to what I am trying to do. Finally I shall paste some relevant code snippets.
I am trying to implement secret key encryption/decryption using the method specified in https://stackoverflow.com/a/992413/171993. If I use that example as-is, it works (although I did notice that I need to re-instantiate the Cipher class, otherwise the decryption produces garbage). However, in my implementation I get the following exception:
java.security.InvalidKeyException: Wrong algorithm: AES or Rijndael required
at com.sun.crypto.provider.AESCrypt.init(AESCrypt.java:77)
at com.sun.crypto.provider.CipherBlockChaining.init(CipherBlockChaining.java:91)
at com.sun.crypto.provider.CipherCore.init(CipherCore.java:469)
at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:217)
at javax.crypto.Cipher.implInit(Cipher.java:790)
at javax.crypto.Cipher.chooseProvider(Cipher.java:848)
at javax.crypto.Cipher.init(Cipher.java:1347)
at javax.crypto.Cipher.init(Cipher.java:1281)
at securitytest.SecurityManager.getCipher(SecurityManager.java:175)
at securitytest.SecurityManager.decryptSecretKey(SecurityManager.java:379)
at securitytest.SecurityManager.<init>(SecurityManager.java:82)
at securitytest.Test.main(Test.java:44)
To beat off the obvious question, yes, I do use the same algorithm: in fact, I assigned AES/CBC/PKCS5Padding to a constant and use that for instantiating both the Cipher class for encryption and decryption. I have also tried using only AES instantiate Cipher for the decryption, but that did not work either.
What I am trying to do is to password-protect a secret key by using AES/CBC/PKCS5Padding. I generate a random salt and initialisation vector. After encrypting the secret key, I append the initialisation vector (an array of bytes) to the encrypted value (also an array of bytes, creating a new array). I then encode this value in Base64 and store it in a Sqlite database, along with the salt (which, for the sake of simplicity, I store as a comma-separated string of values). However when I try to decrypt, I get the above exception. I can verify that directly after my call to the encryption method and directly before the decryption method, the following values are exactly the same (when converted to Base64 so that I can print it out):
The salt
The initialisation vector
The encrypted secret key (i.e. the cipher text)
I have tried both Java 6 and 7: both give the same results. I have also ruled out the unlimited strength jurisdiction policy files as an issue. In fact, I get a similar error if I substitute "AES" with another algorithm and adjust the length of the salt accordingly (for example "Blowfish" with IV length 8, which produces java.security.InvalidKeyException: Wrong algorithm: Blowfish required).
Google has not been able to help me with this problem. If anyone can shed some light on this, I would be very appreciative.
Here are some code snippets (my apologies, it is a little rough):
private static final int INIT_VECTOR_LENGTH = 16;
private static final int PRIVATE_KEY_LENGTH = 128;
private static final int SALT_LENGTH = 16;
private static final int PBE_KEYSPEC_ITERATIONS = 65536;
private static final String CIPHER_ALGORITHM = "AES";
private static final String CIPHER_ALGORITHM_MODE = "CBC";
private static final String CIPHER_ALGORITHM_PADDING = "PKCS5Padding";
private static final String DIGEST = "SHA1";
private static final String PLAINTEXT_ENCODING = "UTF8";
private static final String PRNG = DIGEST + "PRNG";
private static final String SECRET_KEY_FACTORY = "PBKDF2WithHmac" + DIGEST;
private static final String CIPHER = CIPHER_ALGORITHM + "/" + CIPHER_ALGORITHM_MODE + "/" + CIPHER_ALGORITHM_PADDING;
private IvParameterSpec ivSpec;
private final BASE64Encoder encoder = new BASE64Encoder();
private final BASE64Decoder decoder = new BASE64Decoder();
private Cipher getCipher(SecretKey key, int mode) {
Cipher cipher = null;
try {
cipher = Cipher.getInstance(CIPHER);
}
catch (NoSuchAlgorithmException e) {System.err.println(System.err.println(e.getMessage());}
catch (NoSuchPaddingException e) {System.err.println(e.getMessage());}
try {
if (mode == Cipher.ENCRYPT_MODE) {
cipher.init(mode, key);
AlgorithmParameters params = cipher.getParameters();
ivSpec = params.getParameterSpec(IvParameterSpec.class);
}
else {
/* This is my point-of-failure. */
cipher.init(mode, key, ivSpec);
}
}
catch (InvalidKeyException e) {System.err.println(e.getMessage());}
catch (InvalidAlgorithmParameterException e) {System.err.println(e.getMessage());}
catch (InvalidParameterSpecException e) {System.err.println(e.getMessage());}
return cipher;
}
private SecurityData.Secrets generateSecrets(SecretKey decryptedKey, byte[] salt, String passphrase) {
/* Generate a new key for encrypting the secret key. */
byte[] raw = null;
PBEKey key = null;
PBEKeySpec password = new PBEKeySpec(passphrase.toCharArray(), salt, PBE_KEYSPEC_ITERATIONS, PRIVATE_KEY_LENGTH);
SecretKeyFactory factory = null;
byte[] initVector = null;
byte[] secretKeyBytes = decryptedKey.getEncoded();
try {
factory = SecretKeyFactory.getInstance(SECRET_KEY_FACTORY);
key = (PBEKey) factory.generateSecret(password);
}
catch (NoSuchAlgorithmException e) {System.err.println(e.getMessage());}
catch (InvalidKeySpecException e) {System.err.println(e.getMessage());}
SecretKeySpec newKey = new SecretKeySpec(key.getEncoded(), CIPHER_ALGORITHM);
/* Encrypt the secret key. */
IvParameterSpec ivSpec = new IvParameterSpec(initVector);
Cipher cipher = getCipher(newKey, ivSpec, Cipher.ENCRYPT_MODE);
try {
raw = cipher.doFinal(secretKeyBytes);
}
catch (IllegalBlockSizeException e) {System.err.println(e.getMessage());}
catch (BadPaddingException e) {System.err.println(e.getMessage());}
return new SecurityData.Secrets(encoder.encode(concatByteArrays(initVector, raw)), joinByteArray(salt));
}
private SecretKey decryptSecretKey(String encryptedKey, String salt, String passphrase) {
/* Get initialisation vector. */
byte[] raw = null, decoded = null, initVector = new byte[INIT_VECTOR_LENGTH];
try {
decoded = decoder.decodeBuffer(encryptedKey);
} catch (IOException e) {System.err.println(e.getMessage());}
System.arraycopy(decoded, 0, initVector, 0, INIT_VECTOR_LENGTH);
raw = new byte[decoded.length-INIT_VECTOR_LENGTH];
System.arraycopy(decoded, INIT_VECTOR_LENGTH, raw, 0, decoded.length-INIT_VECTOR_LENGTH);
IvParameterSpec ivSpec = new IvParameterSpec(initVector);
/* Generate the key. */
byte[] rawSalt = splitByteArrayString(salt);
PBEKeySpec password = new PBEKeySpec(passphrase.toCharArray(), rawSalt, PBE_KEYSPEC_ITERATIONS, PRIVATE_KEY_LENGTH);
SecretKeyFactory factory = null;
PBEKey key = null;
try {
factory = SecretKeyFactory.getInstance(SECRET_KEY_FACTORY);
key = (PBEKey) factory.generateSecret(password);
}
catch (NoSuchAlgorithmException e) {System.err.println(e.getMessage());}
catch (InvalidKeySpecException e) {System.err.println(e.getMessage());}
Cipher cipher = getCipher(key, Cipher.DECRYPT_MODE);
/* Decrypt the message. */
byte[] stringBytes = null;
try {
stringBytes = cipher.doFinal(raw);
}
catch (IllegalBlockSizeException e) {System.err.println(e.getMessage());}
catch (BadPaddingException e) {System.err.println(e.getMessage());}
/* Converts the decoded message to a String. */
String clear = null;
try {
clear = new String(stringBytes, PLAINTEXT_ENCODING);
}
catch (UnsupportedEncodingException e) {System.err.println(e.getMessage());}
return new SecretKeySpec(clear.getBytes(), CIPHER_ALGORITHM);
}
The SecretKey object needs to return "AES" from its getAlgorithm() method. That's why the example has these steps:
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");