after struggling for a while, I figured that I should bring my question here as I do not really know exactly what I am doing. Basically, I am using a somewhat modified version of the cryptastic version found here.
I am currently base64 encoding everything, then converting to hexidecimal in order to keep the data the same between the two langauges. Right now, I have managed to get decryption from PHP to work, but keep coming across errors when I try to encrypt. I am using BouncyCastle... and for some reason in the decrypt when I do an init, instead of using false it will only work with true (which the docs say is for encryption...)
Anyway, here is my class thus far if anyone has a chance to look at it that would be great.
Technically, decrypt is working only because I have a hack function to remove padding as the ZeroByteEncoding isn't working properly either.
So, I am able to decrypt from the PHP cryptastic, but not encrypt properly so that it can decrypt (it throws an error in my stringToHexidecimal then)
public class Cryptastic {
public final static String CHARSET = "US-ASCII";
public byte[] encrypt(String toEncrypt, byte[] keyBytes, boolean base64Encode)
{
try
{
toEncrypt = serialize(toEncrypt);
byte[] newEncrypt = toEncrypt.getBytes(CHARSET);
BlockCipher blockCipher = new RijndaelEngine(256);
SecureRandom secureRandom = new SecureRandom();
byte[] iv = new byte[32];
secureRandom.nextBytes(iv);
CipherParameters cipherParameters = new ParametersWithIV(new KeyParameter(keyBytes), iv);
RijndaelEngine rijndael = new RijndaelEngine(256);
SICBlockCipher ctrMode = new SICBlockCipher(rijndael);
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(ctrMode, new ZeroBytePadding());
cipher.init(true, cipherParameters);
int size = cipher.getOutputSize(newEncrypt.length);
byte[] encrypted = new byte[size];
System.out.println(displayBytes(encrypted, false));
int outputLength = cipher.processBytes(newEncrypt, 0, newEncrypt.length, encrypted, 0);
cipher.doFinal(encrypted, outputLength);
if(base64Encode)
{
encrypted = Base64.encode(encrypted);
}
byte[] temp = new byte[encrypted.length + 32];
for(int i = 0; i < iv.length; i++)
{
temp[i] = iv[i];
}
for(int i = iv.length; i < temp.length; i++)
{
temp[i] = encrypted[i - iv.length];
}
encrypted = temp;
byte[] mac = generateKey(encrypted, keyBytes, 1000, 32);
temp = new byte[encrypted.length + mac.length];
for(int i = 0; i < encrypted.length; i++)
{
temp[i] = encrypted[i];
}
for(int i = encrypted.length; i < temp.length; i++)
{
temp[i] = mac[i - encrypted.length];
}
return encrypted;
//System.out.println("Original IV: " + displayBytes(iv, false));
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
public byte[] decrypt(String toDecrypt, byte[] keyBytes, boolean base64Encode)
{
try
{
byte[] newDecrypt = hexToByte(toDecrypt);
//toDecrypt = hexToString(toDecrypt);
if(base64Encode)
{
newDecrypt = Base64.decode(newDecrypt);
//toDecrypt = new String(Base64.decode(toDecrypt.getBytes(CHARSET)), CHARSET);
}
byte[] iv = new byte[32];
byte[] extractedMac = new byte[32];
int offset = newDecrypt.length - 32;
for(int i = 0; i < 32; i++)
{
iv[i] = newDecrypt[i];
extractedMac[i] = newDecrypt[i + offset];
}
byte[] temp = new byte[offset - 32];
for(int i = 0; i < temp.length; i++)
{
temp[i] = newDecrypt[i + 32];
}
newDecrypt = temp;
byte[] combo = new byte[newDecrypt.length + iv.length];
for(int i = 0; i < iv.length; i++)
{
combo[i] = iv[i];
}
for(int i = iv.length; i < combo.length; i++)
{
combo[i] = newDecrypt[i - iv.length];
}
byte[] mac = generateKey(new String(combo, CHARSET), new String(keyBytes, CHARSET), 1000, 32);
boolean matches = new String(extractedMac).equals(new String(mac));
if(!matches)
{
byte[] toReturn = new byte[0];
return toReturn;
}
CipherParameters cipherParameters = new ParametersWithIV(new KeyParameter(keyBytes), iv);
RijndaelEngine rijndael = new RijndaelEngine(256);
SICBlockCipher ctrMode = new SICBlockCipher(rijndael);
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(ctrMode, new ZeroBytePadding());
cipher.init(true, cipherParameters);
int size = cipher.getOutputSize(newDecrypt.length);
byte[] decrypted = new byte[size];
System.out.println(displayBytes(newDecrypt, false));
System.out.println(displayBytes(decrypted, false));
int outputLength = cipher.processBytes(newDecrypt, 0, newDecrypt.length, decrypted, 0);
cipher.doFinal(decrypted, outputLength);
System.out.println(displayBytes(decrypted, false));
temp = new byte[newDecrypt.length];
for(int i = 0; i < temp.length; i++)
{
temp[i] = decrypted[i];
}
decrypted = temp;
decrypted = unserialize(new String(decrypted, CHARSET)).getBytes(CHARSET);
return decrypted;
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
public byte[] generateKey(String password, String salt, int blockIterations, int keyLength)
{
try
{
SHA256Digest digest = new SHA256Digest();
int hashLength = digest.getDigestSize();
long keyBlocks = (long) Math.ceil( (double) keyLength / (double) hashLength);
String derived_key = "";
//System.out.println("Number of blocks: " + keyBlocks);
for(long block = 1; block <= keyBlocks; block++)
{
byte[] initialHash = hash_hmac(salt + new String(pack(block)/*, CHARSET */), password);
byte[] compareHash = initialHash;
//System.out.println("Block: " + block);
for(int i = 1; i < blockIterations; i++)
// XOR each iterate
compareHash = hash_hmac(compareHash, password);
for(int j = 0; j < initialHash.length; j++)
{
initialHash[j] = (byte) (initialHash[j] ^ compareHash[j]);
}
}
derived_key += new String(initialHash/*, CHARSET */);
}
return derived_key.substring(0, keyLength).getBytes(CHARSET);
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
public byte[] generateKey(byte[] password, byte[] salt, int blockIterations, int keyLength)
{
try
{
SHA256Digest digest = new SHA256Digest();
int hashLength = digest.getDigestSize();
long keyBlocks = (long) Math.ceil( (double) keyLength / (double) hashLength);
byte[] derived_key = null;
//System.out.println("Number of blocks: " + keyBlocks);
for(long block = 1; block <= keyBlocks; block++)
{
byte[] packed = pack(block);
byte[] combined = new byte[salt.length + packed.length];
for(int i = 0; i < salt.length; i++)
{
combined[i] = salt[i];
}
for(int i = salt.length; i < combined.length; i++)
{
combined[i] = packed[i - salt.length];
}
byte[] initialHash = hash_hmac(combined, password);
byte[] compareHash = initialHash;
//System.out.println("Block: " + block);
for(int i = 1; i < blockIterations; i++)
{
// XOR each iterate
compareHash = hash_hmac(compareHash, password);
for(int j = 0; j < initialHash.length; j++)
{
initialHash[j] = (byte) (initialHash[j] ^ compareHash[j]);
}
}
if(derived_key == null)
{
derived_key = initialHash;
}
else
{
byte[] temp = new byte[derived_key.length + initialHash.length];
for(int i = 0; i < derived_key.length; i++)
{
temp[i] = derived_key[i];
}
for(int i = derived_key.length; i < temp.length; i++)
{
temp[i] = initialHash[i - derived_key.length];
}
derived_key = temp;
}
}
byte[] toReturn = new byte[keyLength];
for(int i = 0; i < toReturn.length; i++)
{
toReturn[i] = derived_key[i];
}
return toReturn;
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
public byte[] pack(long toPack)
{
String test = Long.toHexString(toPack);
while(test.length() < 8)
{
test = "0" + test;
}
byte[] toReturn = new byte[4];
toReturn[0] = Byte.parseByte(test.substring(0, 2));
toReturn[1] = Byte.parseByte(test.substring(2, 4));
toReturn[2] = Byte.parseByte(test.substring(4, 6));
toReturn[3] = Byte.parseByte(test.substring(6, 8));
return toReturn;
}
public String hexToString(String hex)
{
return new String(hexToByte(hex));
}
public byte[] hexToByte(String hex)
{
if(hex.length() % 2 != 0)
{
hex = "0" + hex;
}
byte[] toReturn = new byte[hex.length() / 2];
for(int i = 0; i < toReturn.length; i++)
{
toReturn[i] = Byte.parseByte(hex.substring(i * 2, (i + 1) * 2), 16);
}
//System.out.println(displayBytes(toReturn, false));
return toReturn;
}
public byte[] hash_hmac(String data, String key)
{
return hash_hmac("HmacSHA256", data, key);
}
public byte[] hash_hmac(String algorithm , String data , String key)
{
try
{
SecretKeySpec secret = new SecretKeySpec(key.getBytes(/*CHARSET*/), algorithm);
Mac mac = Mac.getInstance(algorithm);
mac.init(secret);
byte[] digest = mac.doFinal(data.getBytes(/*CHARSET*/));
return digest;
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
public byte[] hash_hmac(byte[] data, String key)
{
return hash_hmac("HmacSHA256", data, key);
}
public byte[] hash_hmac(byte[] data, byte[] key)
{
return hash_hmac("HmacSHA256", data, key);
}
public byte[] hash_hmac(String algorithm , byte[] data , byte[] key)
{
try
{
SecretKeySpec secret = new SecretKeySpec(key, algorithm);
Mac mac = Mac.getInstance(algorithm);
mac.init(secret);
byte[] digest = mac.doFinal(data);
return digest;
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
public byte[] hash_hmac(String algorithm , byte[] data , String key)
{
try
{
SecretKeySpec secret = new SecretKeySpec(key.getBytes(/*CHARSET*/), algorithm);
Mac mac = Mac.getInstance(algorithm);
mac.init(secret);
byte[] digest = mac.doFinal(data);
return digest;
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
public String displayBytes(byte[] bytes, boolean rawDisplay)
{
String toReturn = "";
if(rawDisplay)
{
toReturn = new String(bytes/*, CHARSET */);
}
else
{
for (byte b : bytes)
{
toReturn += String.format("%02x", b);
}
}
return toReturn;
}
public String toHex(String toConvert) {
if(toConvert.getBytes().length == 0)
{
return "";
}
return String.format("%04x", new BigInteger(toConvert.getBytes(/*YOUR_CHARSET?*/)));
}
/*
* This creates a string representation that should line up with PHPs serialize.
*/
public String serialize(String toSerialize)
{
return "s:" + toSerialize.length() + ":\"" + toSerialize + "\";";
}
public String unserialize(String toUnserialize)
{
System.out.println(toUnserialize);
int endIndex = toUnserialize.indexOf("\";");
while (endIndex < toUnserialize.length() && endIndex < toUnserialize.indexOf("\";", endIndex + 1))
{
endIndex = toUnserialize.indexOf("\";", endIndex + 1);
}
return toUnserialize.substring(toUnserialize.indexOf(":\"") + 2, endIndex);
}
Without reading through all the code, if an encrypt works instead of a decrypt, then you may have "decrypted" the plain text to form the cipher text, as symmetric encryption/decryption is reversable. In other words, you could use plain = encrypt(decrypt(plain)) as well as plain = decrypt(encrypt(plain)).
The only possible difference could be padding: decrypt should remove padding and encrypt should add it, so that would make a difference.
I have it working now... entire class is as follows. When using it with the PHP cryptastic, add in conversion to hexidecimal after conversion to base64.
public class Cryptastic {
public final static String CHARSET = "US-ASCII";
public byte[] encrypt(String toEncrypt, byte[] keyBytes, boolean base64Encode)
{
try
{
toEncrypt = serialize(toEncrypt);
byte[] newEncrypt = toEncrypt.getBytes(CHARSET);
BlockCipher blockCipher = new RijndaelEngine(256);
SecureRandom secureRandom = new SecureRandom();
byte[] iv = new byte[32];
secureRandom.nextBytes(iv);
CipherParameters cipherParameters = new ParametersWithIV(new KeyParameter(keyBytes), iv);
RijndaelEngine rijndael = new RijndaelEngine(256);
SICBlockCipher ctrMode = new SICBlockCipher(rijndael);
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(ctrMode, new ZeroBytePadding());
cipher.init(false, cipherParameters);
int size = cipher.getOutputSize(newEncrypt.length);
byte[] padded = new byte[size];
for(int i = 0; i < newEncrypt.length; i++)
{
padded[i] = newEncrypt[i];
}
for(int i = newEncrypt.length; i < size; i++)
{
padded[i] = 0;
}
newEncrypt = padded;
byte[] encrypted = new byte[size];
int outputLength = cipher.processBytes(newEncrypt, 0, newEncrypt.length, encrypted, 0);
cipher.doFinal(encrypted, outputLength);
byte[] temp = new byte[encrypted.length + 32];
for(int i = 0; i < iv.length; i++)
{
temp[i] = iv[i];
}
for(int i = iv.length; i < temp.length; i++)
{
temp[i] = encrypted[i - iv.length];
}
encrypted = temp;
byte[] mac = generateKey(new String(encrypted, CHARSET), new String(keyBytes, CHARSET), 1000, 32);
temp = new byte[encrypted.length + mac.length];
for(int i = 0; i < encrypted.length; i++)
{
temp[i] = encrypted[i];
}
for(int i = encrypted.length; i < temp.length; i++)
{
temp[i] = mac[i - encrypted.length];
}
encrypted = temp;
if(base64Encode)
{
encrypted = Base64.encode(encrypted);
}
return encrypted;
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
public byte[] decrypt(String toDecrypt, byte[] keyBytes, boolean base64Encode)
{
try
{
byte[] newDecrypt = hexToByte(toDecrypt);
if(base64Encode)
{
newDecrypt = Base64.decode(newDecrypt);
}
byte[] iv = new byte[32];
byte[] extractedMac = new byte[32];
int offset = newDecrypt.length - 32;
for(int i = 0; i < 32; i++)
{
iv[i] = newDecrypt[i];
extractedMac[i] = newDecrypt[i + offset];
}
byte[] temp = new byte[offset - 32];
for(int i = 0; i < temp.length; i++)
{
temp[i] = newDecrypt[i + 32];
}
newDecrypt = temp;
byte[] combo = new byte[newDecrypt.length + iv.length];
for(int i = 0; i < iv.length; i++)
{
combo[i] = iv[i];
}
for(int i = iv.length; i < combo.length; i++)
{
combo[i] = newDecrypt[i - iv.length];
}
byte[] mac = generateKey(new String(combo, CHARSET), new String(keyBytes, CHARSET), 1000, 32);
boolean matches = new String(extractedMac).equals(new String(mac));
if(!matches)
{
byte[] toReturn = new byte[0];
return toReturn;
}
CipherParameters cipherParameters = new ParametersWithIV(new KeyParameter(keyBytes), iv);
RijndaelEngine rijndael = new RijndaelEngine(256);
SICBlockCipher ctrMode = new SICBlockCipher(rijndael);
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(ctrMode, new ZeroBytePadding());
cipher.init(true, cipherParameters);
int size = cipher.getOutputSize(newDecrypt.length);
byte[] decrypted = new byte[size];
int outputLength = cipher.processBytes(newDecrypt, 0, newDecrypt.length, decrypted, 0);
cipher.doFinal(decrypted, outputLength);
temp = new byte[newDecrypt.length];
for(int i = 0; i < temp.length; i++)
{
temp[i] = decrypted[i];
}
decrypted = temp;
decrypted = unserialize(new String(decrypted, CHARSET)).getBytes(CHARSET);
return decrypted;
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
public byte[] generateKey(String password, String salt, int blockIterations, int keyLength)
{
try
{
SHA256Digest digest = new SHA256Digest();
int hashLength = digest.getDigestSize();
long keyBlocks = (long) Math.ceil( (double) keyLength / (double) hashLength);
String derived_key = "";
//System.out.println("Number of blocks: " + keyBlocks);
for(long block = 1; block <= keyBlocks; block++)
{
byte[] initialHash = hash_hmac(salt + new String(pack(block)/*, CHARSET */), password);
byte[] compareHash = initialHash;
//System.out.println("Block: " + block);
for(int i = 1; i < blockIterations; i++)
{
// XOR each iterate
compareHash = hash_hmac(compareHash, password);
for(int j = 0; j < initialHash.length; j++)
{
initialHash[j] = (byte) (initialHash[j] ^ compareHash[j]);
}
}
derived_key += new String(initialHash/*, CHARSET */);
}
return derived_key.substring(0, keyLength).getBytes(CHARSET);
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
public byte[] generateKey(byte[] password, byte[] salt, int blockIterations, int keyLength)
{
try
{
SHA256Digest digest = new SHA256Digest();
int hashLength = digest.getDigestSize();
long keyBlocks = (long) Math.ceil( (double) keyLength / (double) hashLength);
byte[] derived_key = null;
for(long block = 1; block <= keyBlocks; block++)
{
byte[] packed = pack(block);
byte[] combined = new byte[salt.length + packed.length];
for(int i = 0; i < salt.length; i++)
{
combined[i] = salt[i];
}
for(int i = salt.length; i < combined.length; i++)
{
combined[i] = packed[i - salt.length];
}
byte[] initialHash = hash_hmac(combined, password);
byte[] compareHash = initialHash;
//System.out.println("Block: " + block);
for(int i = 1; i < blockIterations; i++)
{
// XOR each iterate
compareHash = hash_hmac(compareHash, password);
for(int j = 0; j < initialHash.length; j++)
{
initialHash[j] = (byte) (initialHash[j] ^ compareHash[j]);
}
}
if(derived_key == null)
{
derived_key = initialHash;
}
else
{
byte[] temp = new byte[derived_key.length + initialHash.length];
for(int i = 0; i < derived_key.length; i++)
{
temp[i] = derived_key[i];
}
for(int i = derived_key.length; i < temp.length; i++)
{
temp[i] = initialHash[i - derived_key.length];
}
derived_key = temp;
}
}
byte[] toReturn = new byte[keyLength];
for(int i = 0; i < toReturn.length; i++)
{
toReturn[i] = derived_key[i];
}
return toReturn;
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
public byte[] pack(long toPack)
{
String test = Long.toHexString(toPack);
while(test.length() < 8)
{
test = "0" + test;
}
byte[] toReturn = new byte[4];
toReturn[0] = Byte.parseByte(test.substring(0, 2));
toReturn[1] = Byte.parseByte(test.substring(2, 4));
toReturn[2] = Byte.parseByte(test.substring(4, 6));
toReturn[3] = Byte.parseByte(test.substring(6, 8));
return toReturn;
}
public String hexToString(String hex)
{
return new String(hexToByte(hex));
}
public byte[] hexToByte(String hex)
{
if(hex.length() % 2 != 0)
{
hex = "0" + hex;
}
byte[] toReturn = new byte[hex.length() / 2];
for(int i = 0; i < toReturn.length; i++)
{
toReturn[i] = Byte.parseByte(hex.substring(i * 2, (i + 1) * 2), 16);
}
return toReturn;
}
public byte[] hash_hmac(String data, String key)
{
return hash_hmac("HmacSHA256", data, key);
}
public byte[] hash_hmac(String algorithm , String data , String key)
{
try
{
SecretKeySpec secret = new SecretKeySpec(key.getBytes(/*CHARSET*/), algorithm);
Mac mac = Mac.getInstance(algorithm);
mac.init(secret);
byte[] digest = mac.doFinal(data.getBytes(/*CHARSET*/));
return digest;
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
public byte[] hash_hmac(byte[] data, String key)
{
return hash_hmac("HmacSHA256", data, key);
}
public byte[] hash_hmac(byte[] data, byte[] key)
{
return hash_hmac("HmacSHA256", data, key);
}
public byte[] hash_hmac(String algorithm , byte[] data , byte[] key)
{
try
{
SecretKeySpec secret = new SecretKeySpec(key, algorithm);
Mac mac = Mac.getInstance(algorithm);
mac.init(secret);
byte[] digest = mac.doFinal(data);
return digest;
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
public byte[] hash_hmac(String algorithm , byte[] data , String key)
{
try
{
SecretKeySpec secret = new SecretKeySpec(key.getBytes(/*CHARSET*/), algorithm);
Mac mac = Mac.getInstance(algorithm);
mac.init(secret);
byte[] digest = mac.doFinal(data);
return digest;
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
public String displayBytes(byte[] bytes, boolean rawDisplay)
{
String toReturn = "";
if(rawDisplay)
{
toReturn = new String(bytes/*, CHARSET */);
}
else
{
for (byte b : bytes)
{
toReturn += String.format("%02x", b);
}
}
return toReturn;
}
public String toHex(String toConvert) {
if(toConvert.getBytes().length == 0)
{
return "";
}
return String.format("%04x", new BigInteger(toConvert.getBytes(/*YOUR_CHARSET?*/)));
}
/*
* This creates a string representation that should line up with PHPs serialize.
*/
public String serialize(String toSerialize)
{
return "s:" + toSerialize.length() + ":\"" + toSerialize + "\";";
}
public String unserialize(String toUnserialize)
{
int endIndex = toUnserialize.indexOf("\";");
while (endIndex < toUnserialize.length() && endIndex < toUnserialize.indexOf("\";", endIndex + 1))
{
endIndex = toUnserialize.indexOf("\";", endIndex + 1);
}
return toUnserialize.substring(toUnserialize.indexOf(":\"") + 2, endIndex);
}
}
Related
I have the following java function:
public static String customPasswordFunction(String value, String salt) {
byte[] input = null;
try {
byte[] valueBytes = value.getBytes();
byte[] saltBytes = salt.getBytes();
MessageDigest digest = MessageDigest.getInstance("MD5");
digest.reset();
digest.update(saltBytes);
input = digest.digest(valueBytes);
for(int i=0; i < 1000; i++) {
digest.reset();
input = digest.digest(input);
}
}
catch (NoSuchAlgorithmException ex) {
logger.error(ex.toString(), ex);
return null;
}
//convert digest to alphanumeric
String alphaNumReturn = "";
String tmp = null;
for (int i = 0; i < input.length; i++) {
tmp = (Integer.toHexString(0xFF & input[i]));
if (tmp.length() == 1) {
alphaNumReturn += "0" + tmp;
}
else {
alphaNumReturn += tmp;
}
}
return alphaNumReturn;
}
We have the quick ability in ruby to hash the salt and password as follows:
hashed_password = OpenSSL::Digest::MD5.hexdigest(salt+password)
But how would I re-produce the following java code in ruby?
for(int i=0; i < 1000; i++) {
digest.reset();
input = digest.digest(input);
}
So I don't know Java but it appears this part
digest = MessageDigest.getInstance("MD5");
digest.reset();
digest.update(saltBytes);
input = digest.digest(valueBytes);
for(int i=0; i < 1000; i++) {
digest.reset();
input = digest.digest(input);
}
Should translate to
require 'digest'
digest = Digest::MD5.new
digest.reset.update(salt)
input = digest.digest(value)
input = 1000
.times
.reduce(input) do |input,_|
digest.reset.digest(input)
end
Docs:
Digest::MD5
Integer#times
Enumerable#reduce
As a sidenote: MD5 is a hashing algorithm not an encryption.
The java code below generates a SHA-256 hash of the input msg, using the key. However, all my attempts to write code that does same operation in C# have not yielded the same results given same input. I would need help getting the C# equivalent as I have tried a lot already with little success.
I think I've been able to translate most of the code into C# correctly, apart from the part which updates the digest (m.update()), first with the key, then later with the message before hashing.
JAVA CODE
public static String generateHash256Value(String msg, String key) {
MessageDigest m = null;
String hashText = null;
System.out.println("Value to hash::::::::::" + msg);
byte[] actualKeyBytes = HexToByte(secret_key);
try {
m = MessageDigest.getInstance("SHA-256");
m.update(actualKeyBytes, 0, actualKeyBytes.length);
try {
m.update(msg.getBytes("UTF-8"), 0, msg.length());
}
catch (UnsupportedEncodingException ex) {
ex.printStackTrace();
}
hashText = new BigInteger(1, m.digest()).toString(16);
if (hashText.length() < 64) { //must be 64 in length
int numberOfZeroes = 64 - hashText.length();
String zeroes = "";
for (int i = 0; i < numberOfZeroes; i++) {
zeroes = zeroes + "0";
}
hashText = zeroes + hashText;
}
}
catch (NoSuchAlgorithmException ex) {
ex.printStackTrace();
}
hashText = hashText.toUpperCase();
return hashText;
}
public static byte[] hex2Byte(String str) {
byte[] bytes = new byte[str.length() / 2];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) Integer
.parseInt(str.substring(2 * i, 2 * i + 2), 16);
}
return bytes;
}
C# CODE (attempt)
private static string DoSpecialSha256Hash(string message, string key)
{
String hashText = null;
Console.WriteLine("Value to hash::::::::::" + message);
byte[] keyByte = hex2Byte(key);
Encoding encoder = new System.Text.UTF8Encoding();
var hashAlgo = new SHA256Managed();
var messageBytes = encoder.GetBytes(message);
var toDigest = Combine(keyByte, messageBytes);
hashText = ByteToString(hashAlgo.ComputeHash(toDigest, 0, message.Length));
if (hashText.Length < 64)
{ //must be 64 in length
int numberOfZeroes = 64 - hashText.Length;
String zeroes = "";
for (int i = 0; i < numberOfZeroes; i++)
{
zeroes = zeroes + "0";
}
hashText = zeroes + hashText;
}
hashText = hashText.ToUpper();
return hashText;
}
public static byte[] HexToByte(String hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
private static string ByteToString(byte[] buff)
{
string sbinary = "";
for (int i = 0; i < buff.Length; i++)
{
sbinary += buff[i].ToString("X2"); // hex format
}
return (sbinary);
}
private static byte[] Combine(params byte[][] arrays)
{
byte[] rv = new byte[arrays.Sum(a => a.Length)];
int offset = 0;
foreach (byte[] array in arrays)
{
System.Buffer.BlockCopy(array, 0, rv, offset, array.Length);
offset += array.Length;
}
return rv;
}
Thanks,
I'm getting a Triple DES decrypted string from the clients server, which has been coded in c# (see below):
using System.IO;
using System;
using System.Security.Cryptography;
using System.Collections;
using System.Text;
class Program
{
static void Main()
{
Console.WriteLine("Hello, World!");
var encryption = TripleDESEncrypt("12345678901234", "C9AF269DF8A78A06D1216BFFF8F0536A");
Console.WriteLine(encryption);
}
public static string TripleDESEncrypt(string strClearText, string strKey)
{
byte[] bytClearText;
byte[] bytClearTextChunk = new byte[8];
byte[] bytEncryptedChunk = new byte[8];
int BytesCount = 0;
int nArrayPosition = 0;
string strEncryptedChar;
string strEncryptedText = "";
ArrayList Input = new ArrayList();
ArrayList Output = new ArrayList();
TripleDESCryptoServiceProvider tdes = (TripleDESCryptoServiceProvider)TripleDESCryptoServiceProvider.Create();
tdes.Key = HexToByteArray(strKey);
tdes.Mode = CipherMode.ECB;
ICryptoTransform tdesEncrypt = tdes.CreateEncryptor();
bytClearText = ASCIIEncoding.ASCII.GetBytes(strClearText);
BytesCount = bytClearText.Length;
for (int i = 0; i < BytesCount; i++)
{
if (nArrayPosition == 8)
{
Input.Add(bytClearTextChunk);
bytClearTextChunk = new byte[8];
nArrayPosition = 0;
}
bytClearTextChunk[nArrayPosition] = bytClearText[i];
nArrayPosition++;
}
if (nArrayPosition != 0)
Input.Add(bytClearTextChunk);
foreach (byte[] Cbyte in Input)
{
tdesEncrypt.TransformBlock(Cbyte, 0, 8, bytEncryptedChunk, 0);
Output.Add(bytEncryptedChunk);
bytEncryptedChunk = null;
bytEncryptedChunk = new byte[8];
}
foreach (byte[] Cbyte in Output)
{
foreach (byte BByte in Cbyte)
{
strEncryptedChar = BByte.ToString("X");
strEncryptedChar = strEncryptedChar.PadLeft(2, Convert.ToChar("0"));
strEncryptedText += strEncryptedChar;
}
}
return strEncryptedText;
}
private static byte[] HexToByteArray(string strHex)
{
byte[] bytArray = new byte[strHex.Length / 2];
int positionCount = 0;
for (int i = 0; i < strHex.Length; i += 2)
{
bytArray[positionCount] = byte.Parse(strHex.Substring(i, 2), System.Globalization.NumberStyles.HexNumber);
positionCount++;
}
return bytArray;
}
}
I am then trying to Triple DES decrypt it in Java using this key: C9AF269DF8A78A06D1216BFFF8F0536A
Here is my code to decrypt:
public String DesDecryptPin(String pin, String encryptKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException {
String UNICODE_FORMAT = "UTF8";
String decryptedPinText = null;
byte[] hexConvert = hexStringtoByteArray(encryptKey);
SecretKey desKey = null;
byte[] tdesKey = new byte[24];
System.arraycopy(hexConvert, 0, tdesKey, 0,16);
System.arraycopy(hexConvert, 0, tdesKey, 0,8);
byte[] encryptKeyBytes = encryptKey.getBytes(UNICODE_FORMAT);
KeySpec desKeySpec = new DESedeKeySpec(tdesKey);
Cipher desCipher;
SecretKeyFactory skf = SecretKeyFactory.getInstance("DESede");
desCipher = Cipher.getInstance("DESede/ECB/NoPadding");
try {
desKey = skf.generateSecret(desKeySpec);
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
desCipher.init(Cipher.DECRYPT_MODE, desKey);
byte[] decryptPin = desCipher.doFinal(pin.getBytes());
decryptedPinText = new String(decryptPin, "UTF-8");
return decryptedPinText;
}
The sample out put would be input/output would be "12345678901234" however, I'm getting jumbled nonsense returned e.g ��0�8��/0��
So something is getting lost between c# and java...
This is a follow on from a previous question I asked here
I'd appreciate help on this
changes to code
public String DesDecryptPin(String pin, String encryptKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException {
String UNICODE_FORMAT = "UTF8";
String decryptedPinText = null;
SecretKey desKey = null;
byte[] encryptKeyBytes = EncodingUtils.getAsciiBytes(encryptKey);
byte[] tdesKey = new byte[24];
System.arraycopy(encryptKeyBytes, 8, tdesKey, 0, 8);
System.arraycopy(encryptKeyBytes, 0, tdesKey, 8, 16);
KeySpec desKeySpec = new DESedeKeySpec(tdesKey);
Cipher desCipher;
SecretKeyFactory skf = SecretKeyFactory.getInstance("DESede");
desCipher = Cipher.getInstance("DESede/ECB/NoPadding");
try {
desKey = skf.generateSecret(desKeySpec);
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
desCipher.init(Cipher.DECRYPT_MODE, desKey);
byte[] decryptPin = desCipher.doFinal(EncodingUtils.getAsciiBytes(pin));
decryptedPinText = new String(decryptPin, "ASCII");
return decryptedPinText;
}
c# decrypt code
using System.IO;
using System;
using System.Security.Cryptography;
using System.Collections;
using System.Text;
class Program
{
static void Main()
{
Console.WriteLine("Hello, World!");
var encryption = TripleDESDecrypt("1D30CC3DE1641D7F5E821D13FC1200C3", "C9AF269DF8A78A06D1216BFFF8F0536A");
Console.WriteLine(encryption);
}
public static string TripleDESDecrypt(string strEncryptedText, string strKey)
{
string errorMessage = "";
int errorCode = 0;
string strDecryptedText = "";
try
{
byte[] bytEncryptedChunk = new byte[8];
byte[] bytClearTextChunk = new byte[8];
byte[] _bytesEmpty = new byte[8];
int BytesCount = 0;
int positionCount = 0;
ArrayList Input = new ArrayList();
ArrayList Output = new ArrayList();
TripleDESCryptoServiceProvider tdes = (TripleDESCryptoServiceProvider)TripleDESCryptoServiceProvider.Create();
tdes.Key = HexToByteArray(strKey);
tdes.Mode = CipherMode.ECB;
ICryptoTransform tdesDecrypt = tdes.CreateDecryptor();
BytesCount = strEncryptedText.Length;
for (int i = 0; i < BytesCount; i += 2)
{
if (positionCount == 8)
{
positionCount = 0;
Input.Add(bytEncryptedChunk);
bytEncryptedChunk = new byte[8];
}
bytEncryptedChunk[positionCount] = byte.Parse(strEncryptedText.Substring(i, 2), System.Globalization.NumberStyles.HexNumber);
positionCount++;
}
if (positionCount != 0)
{
Input.Add(bytEncryptedChunk);
}
foreach (byte[] Cbyte in Input)
{
tdesDecrypt.TransformBlock(Cbyte, 0, 8, _bytesEmpty, 0);
tdesDecrypt.TransformBlock(Cbyte, 0, 8, bytClearTextChunk, 0);
Output.Add(bytClearTextChunk);
bytClearTextChunk = null;
bytClearTextChunk = new byte[8];
}
foreach (byte[] Cbyte in Output)
{
strDecryptedText += ASCIIEncoding.ASCII.GetString(Cbyte);
}
}
catch (Exception ex)
{
errorCode = 1;
errorMessage = ex.Message;
}
Console.WriteLine(strDecryptedText);
return strDecryptedText;
}
private static byte[] HexToByteArray(string strHex)
{
byte[] bytArray = new byte[strHex.Length / 2];
int positionCount = 0;
for (int i = 0; i < strHex.Length; i += 2)
{
bytArray[positionCount] = byte.Parse(strHex.Substring(i, 2), System.Globalization.NumberStyles.HexNumber);
positionCount++;
}
return bytArray;
}
}
This returns what is inputting into the encrypt 12345678901234
In your C# code, you use ASCII:
bytClearText = ASCIIEncoding.ASCII.GetBytes(strClearText);
While in Java you use UNICODE:
byte[] encryptKeyBytes = encryptKey.getBytes(UNICODE_FORMAT);
Try to change your C# to use UNICODE or your java code to use ASCII.
Also, since the C# is padding the output :
strEncryptedChar = strEncryptedChar.PadLeft(2, Convert.ToChar("0"));
You probably must check to remove all the '00' in the crypted string, so 1D30CC3DE1641D7F5E821D13FC1200C3 will become 1D30CC3DE1641D7F5E821D13FC12C3
(you must check if it's in the boundaries of an hex expression: 1C01A1 should probably be modified since it got a padding on the second Hexa 1C 01 A1: 1C1A1
acording https://stackoverflow.com/a/33768305/1140304 you can use
unicode instead of UTF-8 in java code
encrypt in c# :
public static string Encrypt2(string clearText,string key)
{
try
{
string encryptedText = "";
MD5 md5 = new MD5CryptoServiceProvider();
TripleDES des = new TripleDESCryptoServiceProvider();
des.KeySize = 128;
des.Mode = CipherMode.CBC;
des.Padding = PaddingMode.PKCS7;
byte[] md5Bytes = md5.ComputeHash(Encoding.Unicode.GetBytes(key));
byte[] ivBytes = new byte[8];
des.Key = md5Bytes;
des.IV = ivBytes;
byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
ICryptoTransform ct = des.CreateEncryptor();
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(clearBytes, 0, clearBytes.Length);
cs.Close();
}
encryptedText = Convert.ToBase64String(ms.ToArray());
}
return encryptedText;
}
catch (Exception exception)
{
return "";
}
}
for decode in c# you can use:
public static string Decrypt2(string cipher,string key)
{
try
{
byte[] clearBytes = Convert.FromBase64String(cipher);
MD5 md5 = new MD5CryptoServiceProvider();
byte[] md5Bytes = md5.ComputeHash(Encoding.Unicode.GetBytes(key));
string encryptedText = "";
TripleDES des = new TripleDESCryptoServiceProvider();
des.KeySize = 128;
des.Mode = CipherMode.CBC;
des.Padding = PaddingMode.PKCS7;
byte[] ivBytes = new byte[8];
des.Key = md5Bytes;
des.IV = ivBytes;
ICryptoTransform ct = des.CreateDecryptor();
byte[] resultArray = ct.TransformFinalBlock(clearBytes, 0, clearBytes.Length);
encryptedText = Encoding.Unicode.GetString(resultArray);
return encryptedText;
}
catch (Exception exception)
{
return "";
}
}
now, for encrypt in java you can use :
private String _encrypt2(String clearText,String key )
{
try
{
/**
* create md5
*/
MessageDigest md = MessageDigest.getInstance("md5");
byte[] digestOfPassword = md.digest(key.getBytes("UTF-16LE"));
byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8; )
{
keyBytes[k++] = keyBytes[j++];
}
SecretKey secretKey = new SecretKeySpec(keyBytes, 0, 24, "DESede");
IvParameterSpec iv = new IvParameterSpec(new byte[8]);
Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
byte[] plainTextBytes = clearText.getBytes("UTF-16LE");
byte[] cipherText = cipher.doFinal(plainTextBytes);
String output = Base64.encodeToString(cipherText,Base64.DEFAULT);
return output;
}
catch (Exception ex) {}
return "";
}
and for decrypt in java :
private String _decrypt2(String encryptText,String key)
{
MessageDigest md = null;
byte[] digestOfPassword = null;
try
{
byte[] message = Base64.decode(encryptText.getBytes("UTF-16LE"), Base64.DEFAULT);
/**
* make md5
*/
md = MessageDigest.getInstance("md5");
digestOfPassword = md.digest(key.getBytes("UTF-16LE"));
byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8; )
{
keyBytes[k++] = keyBytes[j++];
}
SecretKey secretKey = new SecretKeySpec(keyBytes, 0, 24, "DESede");
IvParameterSpec iv = new IvParameterSpec(new byte[8]);
Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
byte[] cipherText = cipher.doFinal(message);
return new String(cipherText, "UTF-16LE");
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
catch (InvalidKeyException e)
{
e.printStackTrace();
}
catch (InvalidAlgorithmParameterException e)
{
e.printStackTrace();
}
catch (NoSuchPaddingException e)
{
e.printStackTrace();
}
catch (BadPaddingException e)
{
e.printStackTrace();
}
catch (IllegalBlockSizeException e)
{
e.printStackTrace();
}
return "";
}
If someone find himself/herself in the same problem like I did, here is a java implementation (android) of the same .NET decrypt function:
public static byte[] byteArrayConcat(byte[] array1, byte[] array2) {
byte[] result = new byte[array1.length + array2.length];
System.arraycopy(array1, 0, result, 0, array1.length);
System.arraycopy(array2, 0, result, array1.length, array2.length);
return result;
}
private byte[] fGPKeyTo3DESKey(byte[] GPKey) {
byte[] _3DESKey = new byte[24];
byte[] tmp = new byte[8];
arraycopy(GPKey, 0, tmp, 0, 8);
_3DESKey = DaPlugUtils.byteArrayConcat(GPKey, tmp);
return _3DESKey;
}
private static byte[] hexStringtoByteArray(String hex) {
int len = hex.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + Character.digit(hex.charAt(i + 1), 16));
}
return data;
}
public String desDecryptPin(String pin, String encryptKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidKeySpecException {
int bytesCount = 0;
int positionCount = 0;
byte[] bytEncryptedChunk = new byte[8];
ArrayList<byte[]> Input = new ArrayList();
bytesCount = pin.length();
for (int i = 0; i < bytesCount; i += 2) {
if (positionCount == 8) {
positionCount = 0;
Input.add(bytEncryptedChunk);
bytEncryptedChunk = new byte[8];
}
bytEncryptedChunk[positionCount] = (byte) (Integer.parseInt(pin.substring(i, i + 2), 16));
positionCount++;
}
if (positionCount != 0) {
Input.add(bytEncryptedChunk);
}
byte[] _3DESKey = fGPKeyTo3DESKey(hexStringtoByteArray(encryptKey));
DESedeKeySpec keySpec = new DESedeKeySpec(_3DESKey);
SecretKey k = SecretKeyFactory.getInstance("DESede").generateSecret(keySpec);
Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, k);
String res = "";
for (byte[] bs : Input) {
byte[] decryptPin = cipher.doFinal(bs);
String a = new String(decryptPin, StandardCharsets.US_ASCII);
res += a;
}
return res.trim();
}
In order to properly understand the operation of the standard security handler in pdf
I am trying to reconstruct the entry /o e /u of the encryption dictionary from a previously encrypted pdf.
The encryption dictionary over /o and /u contains:
/P -1852
/CF
<<
/StdCF
<<
/AuthEvent /DocOpen
/Length 16
/CFM/V2
>>
>>
/R 4
/StmF /StdCF
/Filter/Standard
I managed to successfully reconstruct /o but I can not rebuild /u
main:
public static void main(String[] args) {
String docID;
byte[] userPass=null;
byte[] ownerPass=null;
try {
userPass = userPassString.getBytes("ISO-8859-1");
ownerPass = ownerPassString.getBytes("ISO-8859-1");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
try {
PDDocument doc=PDDocument.load(outPDfDir + "/EncryptedDocument_rc4_128.pdf");
if (doc.isEncrypted()){
EncryptDocument encrypt = new EncryptDocument(userPass,ownerPass,outPDfDir,doc.getEncryptionDictionary().getLength());
encrypt.setRevision(doc.getEncryptionDictionary().getRevision());
encrypt.setVersion(doc.getEncryptionDictionary().getVersion());
COSArray cosArray = doc.getDocument().getDocumentID();
docID=cosArray.getString(0); //only first entry of the array
byte[][] keys = encrypt.computeKeys(userPass, ownerPass, doc.getEncryptionDictionary().getPermissions(), docID.getBytes());
byte[] originalUserKey = doc.getEncryptionDictionary().getUserKey();
byte[] originalownerKey = doc.getEncryptionDictionary().getOwnerKey();
System.out.println(toHex(originalUserKey));
System.out.println(toHex(keys[1]));
if (Arrays.equals(originalUserKey,keys[1])){
System.out.println("User correctly authenticated");
}else{
System.out.println("wrong user password");
}
if (Arrays.equals(originalownerKey,keys[0])){
System.out.println("Owner correctly authenticated");
}else{
System.out.println("wrong user password");
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
Encrypt class
public class EncryptDocument {
private ARCFour rc4 = new ARCFour();
public static final byte[] ENCRYPT_PADDING = new byte[]{(byte)40, (byte)-65, (byte)78, (byte)94, (byte)78, (byte)117, (byte)-118, (byte)65, (byte)100, (byte)0, (byte)78, (byte)86, (byte)-1, (byte)-6, (byte)1, (byte)8, (byte)46, (byte)46, (byte)0, (byte)-74, (byte)-48, (byte)104, (byte)62, (byte)-128, (byte)47, (byte)12, (byte)-87, (byte)-2, (byte)100, (byte)83, (byte)105, (byte)122};
byte[] userPass;
byte[] ownerPass;
String destPath;
int revision;
int version;
private static final byte[] pad = new byte[]{(byte)40, (byte)-65, (byte)78, (byte)94, (byte)78, (byte)117, (byte)-118, (byte)65, (byte)100, (byte)0, (byte)78, (byte)86, (byte)-1, (byte)-6, (byte)1, (byte)8, (byte)46, (byte)46, (byte)0, (byte)-74, (byte)-48, (byte)104, (byte)62, (byte)-128, (byte)47, (byte)12, (byte)-87, (byte)-2, (byte)100, (byte)83, (byte)105, (byte)122};
private int keyLength;
public EncryptDocument(byte[] userPass, byte[] ownerPass, String destPath,int keylength) {
this.userPass = userPass;
this.ownerPass = ownerPass;
this.destPath = destPath;
this.keyLength=keylength;
}
public byte[][] computeKeys(byte[] userPass, byte[] ownPass, int permissions, byte[] documentId) {
//pad both user and owner pass
byte[] userPad = padPassword(userPass);
byte[] ownerPad = padPassword(ownPass);
byte[][] data = new byte[2][32];
byte[] userKey = new byte[0];
byte[] encryptionKey;
permissions= computePermissions(permissions);
byte[] ownerKey = new byte[0];
try {
ownerKey = computeOwnerKey(userPad, ownerPad);
encryptionKey = computeEncryptionKey(userPad,ownerKey,documentId,permissions);
userKey = computeUserKey(userPad,ownerKey,permissions,documentId,encryptionKey);
} catch (IOException e) {
e.printStackTrace();
}
data[0]=ownerKey;
data[1]=userKey;
return data;
}
private byte[] computeUserKey(byte[] userPad, byte[] ownerKey, int permissions, byte[] documentId, byte[] mkey) throws IOException {
//algorithm 5
byte[] digest;
ByteArrayOutputStream userKey = new ByteArrayOutputStream();
ARCFour rc4 = new ARCFour();
MessageDigest md5 = null;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
md5.update(pad); // step b
digest = md5.digest(documentId); //c
//build the input of rc4 encryption
userKey.write(digest);
byte[] iterationKey = new byte[mkey.length];
//encrypt it first time plus other 19 times
for (int i = 0; i < 20; ++i) {
System.arraycopy(mkey, 0, iterationKey, 0, iterationKey.length);
for (int input = 0; input < iterationKey.length; ++input) {
iterationKey[input] = (byte) (iterationKey[input] ^ i);
}
rc4.setKey(iterationKey);
ByteArrayInputStream tmpRes = new ByteArrayInputStream(userKey.toByteArray());
userKey.reset();
rc4.write(tmpRes, userKey);
}
return userKey.toByteArray();
}
private byte[] computeEncryptionKey(byte[] userPad,byte[] ownerKey, byte[] documentId,int permissions){
//initialize hash function
MessageDigest md5 = null;
byte[] encryptedKey =new byte[keyLength / 8];
byte[] digest;
try {
md5 = MessageDigest.getInstance("MD5"); //md5.reset()
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
md5.update(userPad);///pass the padded user password (step b)
md5.update(ownerKey); //pass o entry to the hash function (step c)
byte[] ext = int_to_bb_le(permissions); //new byte[]{(byte)permissions, (byte)(permissions >> 8), (byte)(permissions >> 16), (byte)(permissions >> 24)};
md5.update(ext, 0, 4); //pass permission to the hash function stp d
md5.update(documentId); //pass first element of document id (step e)
if (this.revision>=4)
md5.update(new byte[]{(byte)-1, (byte)-1, (byte)-1, (byte)-1}); //metadata not encryped --> add padding (step f)
digest = md5.digest();
//compute encryption key step g
if(revision == 3 || revision == 4) { //do 50 times step h
for(int k = 0; k < 50; ++k) {
md5.reset();
md5.update(digest,0,keyLength / 8);
digest=md5.digest();
}
}
System.arraycopy(digest, 0, encryptedKey, 0, encryptedKey.length);
return encryptedKey;
}
private byte[] computeOwnerKey(byte[] userPad, byte[] ownerPad) throws IOException {
MessageDigest md5 = null;
ARCFour rc4 = new ARCFour();
try {
md5 = MessageDigest.getInstance("MD5"); //md5.reset()
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
byte[] digest = md5.digest(ownerPad);
byte[] mkey = new byte[this.keyLength / 8];
int i;
for (i = 0; i < 50; ++i) {
md5.update(digest, 0, mkey.length);
System.arraycopy(md5.digest(), 0, digest, 0, mkey.length);
}
//encrypt the padded user password using the result of md5 computation on owner padded password
//revision >3 ==> do it 19 times
ByteArrayOutputStream ownerKey = new ByteArrayOutputStream();
//System.arraycopy(userPad, 0, ownerKey, 0, 32);
rc4.write(new ByteArrayInputStream(userPad), ownerKey);
byte[] iterationKey = new byte[mkey.length];
for (i = 0; i < 20; ++i) {
System.arraycopy(mkey, 0, iterationKey, 0, mkey.length);
//prepare encryption key bitwhise xor between encrypted owner padded password and iteration counter
for (int j = 0; j < iterationKey.length; ++j) {
iterationKey[j] = (byte) (digest[j] ^ i);
}
//encrypt with arc4
rc4.setKey(iterationKey);
ByteArrayInputStream tmpres = new ByteArrayInputStream(ownerKey.toByteArray());
ownerKey.reset();
rc4.write(tmpres, ownerKey);
}
//at the 19 invocation the own key is obtained
return ownerKey.toByteArray();
}
public int computePermissions(int permissions){
permissions |= this.revision != 3 && this.revision != 4 && this.revision != 5?-64:-3904;
permissions &= -4;
return permissions;
}
public byte[] padPassword(byte[] password) {
byte[] userPad = new byte[32];
if(password == null) {
System.arraycopy(pad, 0, userPad, 0, 32);
} else {
System.arraycopy(password, 0, userPad, 0, Math.min(password.length, 32));
if(password.length < 32) {
System.arraycopy(pad, 0, userPad, password.length, 32 - password.length);
}
}
return userPad;
}
public static byte[] int_to_bb_le(int myInteger){
return ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(myInteger).allocatelocaterray();
}
}
someone is able to understand where I'm wrong?
I have a requirement to AES Encrypt (with initial vector) an NSString value.But it gives different output on comparing with the java code provided below.In Java it is delivering the correct result.What could be the reason?
For that I have used the code below:
iOS
- (NSData *)AES256EncryptWithKey:(NSString *)key
{
// 'key' should be 32 bytes for AES256, will be null-padded otherwise
char keyPtr[kCCKeySizeAES128+1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
// fetch key data
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [self length];
//See the doc: For block ciphers, the output size will always be less than or
//equal to the input size plus the size of one block.
//That's why we need to add the size of one block here
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES128 ,
INTIAL_VECTOR /* initialization vector (optional) */,
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
free(buffer); //free the buffer;
return nil;
}
- (NSString*)hexStringFromData:(NSData *)data
{
unichar* hexChars = (unichar*)malloc(sizeof(unichar) * (data.length*2));
unsigned char* bytes = (unsigned char*)data.bytes;
for (NSUInteger i = 0; i < data.length; i++) {
unichar c = bytes[i] / 16;
if (c < 10) c += '0';
else c += 'a' - 10;
hexChars[i*2] = c;
c = bytes[i] % 16;
if (c < 10) c += '0';
else c += 'a' - 10;
hexChars[i*2+1] = c;
}
NSString* retVal = [[NSString alloc] initWithCharactersNoCopy:hexChars
length:data.length*2
freeWhenDone:YES];
return [retVal autorelease];
}
The implementation snippet is :
NSData *InputData = [#"mahi" dataUsingEncoding:NSUTF8StringEncoding];
NSData *encryptedData = [InputData AES256EncryptWithKey:ENCRYPTION_KEY];
NSString *encryptedHexString = [self hexStringFromData:encryptedData];
NSLog(#"Encrypted HexString : %#",encryptedHexString);
Java
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class MCrypt {
private String iv = "fedcba9876543210";
private IvParameterSpec ivspec;
private SecretKeySpec keyspec;
private Cipher cipher;
private String SecretKey = "0123456789abcdef";
public MCrypt()
{
ivspec = new IvParameterSpec(iv.getBytes());
keyspec = new SecretKeySpec(SecretKey.getBytes(), "AES");
try {
cipher = Cipher.getInstance("AES/CBC/NoPadding");
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public byte[] encrypt(String text) throws Exception
{
if(text == null || text.length() == 0)
throw new Exception("Empty string");
byte[] encrypted = null;
try {
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
encrypted = cipher.doFinal(padString(text).getBytes());
} catch (Exception e)
{
throw new Exception("[encrypt] " + e.getMessage());
}
return encrypted;
}
public byte[] decrypt(String code) throws Exception
{
if(code == null || code.length() == 0)
throw new Exception("Empty string");
byte[] decrypted = null;
try {
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
decrypted = cipher.doFinal(hexToBytes(code));
} catch (Exception e)
{
throw new Exception("[decrypt] " + e.getMessage());
}
return decrypted;
}
public static String bytesToHex(byte[] data)
{
if (data==null)
{
return null;
}
int len = data.length;
String str = "";
for (int i=0; i<len; i++) {
if ((data[i]&0xFF)<16)
str = str + "0" + java.lang.Integer.toHexString(data[i]&0xFF);
else
str = str + java.lang.Integer.toHexString(data[i]&0xFF);
}
return str;
}
public static byte[] hexToBytes(String str) {
if (str==null) {
return null;
} else if (str.length() < 2) {
return null;
} else {
int len = str.length() / 2;
byte[] buffer = new byte[len];
for (int i=0; i<len; i++) {
buffer[i] = (byte) Integer.parseInt(str.substring(i*2,i*2+2),16);
}
return buffer;
}
}
private static String padString(String source)
{
char paddingChar = ' ';
int size = 16;
int x = source.length() % size;
int padLength = size - x;
for (int i = 0; i < padLength; i++)
{
source += paddingChar;
}
return source;
}
}