New to bouncyCastle, any help appreciated. I am trying to decrypt a file encrypted by third party on my system using bounncycastle java API. It seems to decrypt file fine except for the blob of junk data at the beginning on the decrypted file.Code below
PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher(new CBCBlockCipher(
new AESEngine()));
CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(DatatypeConverter.parseHexBinary(keyInfo.getKey())),
DatatypeConverter.parseHexBinary(keyInfo.getInitializationVector()));
aes.init(false, ivAndKey);
byte[] decryptedBytes = cipherData(aes, Base64.decodeBase64(inputStreamToByteArray(new FileInputStream(encryptedFile))));
return new ByteArrayInputStream(decryptedBytes);
private static byte[] cipherData(PaddedBufferedBlockCipher cipher, byte[] data)
throws Exception {
int minSize = cipher.getOutputSize(data.length);
byte[] outBuf = new byte[minSize];
int length1 = cipher.processBytes(data, 0, data.length, outBuf, 0);
int length2 = cipher.doFinal(outBuf, length1);
int actualLength = length1 + length2;
byte[] result = new byte[actualLength];
System.arraycopy(outBuf, 0, result, 0, result.length);
return result;
}
private byte[] inputStreamToByteArray(InputStream is) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int numberRead;
byte[] data = new byte[16384];
while ((numberRead = is.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, numberRead);
}
buffer.flush();
return buffer.toByteArray();
}
Decrypted data blob looks fine except for the beginning
"???&??ovKw?????C??:?8?06??85042| | "
The openssl command to decrypt the file works fine command below. In fact I am using the key and iv printed out by openssl when decrypting.
openssl aes-256-cbc -d -salt -in encryptedfile.txt -pass pass:password -a -p
The solution is simple: skip the first 16 bytes of the ciphertext blob. The encrypted blob starts with a magic (you can try and read the first 8 bytes as ASCII text), then 8 bytes of random salt that are used together with the password to derive the key and the IV (using an OpenSSL proprietary password hashing mechanism called EVP_BytesToKey).
Because the previous block is used as a vector for the next block in CBC the followup block of 16 bytes is also affected, giving you 32 random bytes at the start. Instead byte 16 to 31 should have been XOR'ed with the IV.
Here's a Java implementation of BytesToKey posted by using my old nickname.
Related
I export a database with mysqldump a database in Ubuntu with java, then I encrypt and decrypt it with Java. I doing that with the following classes Encrypt and Decrypt with Java. But after the decryption some characters at the start of the file is wrong. Here is the problem:
At the first image is the file which programmatically have mysqldump, encrypt and decrypt. At the second one is just the mysqldump from the same command line. Can you point me the direction what to do? Thanks
EDIT
I have create a salt and stored it in a file like this:
Encryption:
FileInputStream saltFis = new FileInputStream("salt.enc");
byte[] salt = new byte[8];
saltFis.read(salt);
saltFis.close();
// reading the iv
FileInputStream ivFis = new FileInputStream("iv.enc");
byte[] iv = new byte[16];
ivFis.read(iv);
ivFis.close();
SecretKeyFactory factory = SecretKeyFactory.getInstance(secretAlgorithm1);
KeySpec keySpec = new PBEKeySpec(rsaSecret.toCharArray(), salt, 65536, 256);
SecretKey secretKey = factory.generateSecret(keySpec);
SecretKey secret = new SecretKeySpec(secretKey.getEncoded(), secretAlgorithm2);
//
Cipher cipher = Cipher.getInstance(algorithmEncryption);
cipher.init(Cipher.ENCRYPT_MODE, secret);
// file encryption
byte[] input = new byte[64];
int bytesRead;
while ((bytesRead = inFile.read(input)) != -1) {
byte[] output = cipher.update(input, 0, bytesRead);
if (output != null)
outFile.write(output);
}
byte[] output = cipher.doFinal();
if (output != null)
outFile.write(output);
inFile.close();
outFile.flush();
outFile.close();
Decryption:
FileInputStream saltFis = new FileInputStream("salt.enc");
byte[] salt = new byte[8];
saltFis.read(salt);
saltFis.close();
// reading the iv
FileInputStream ivFis = new FileInputStream("iv.enc");
byte[] iv = new byte[16];
ivFis.read(iv);
ivFis.close();
SecretKeyFactory factory = SecretKeyFactory.getInstance(secretAlgorithm1);
KeySpec keySpec = new PBEKeySpec(rsaSecret.toCharArray(), salt, 65536, 256);
SecretKey secretKey = factory.generateSecret(keySpec);
SecretKey secret = new SecretKeySpec(secretKey.getEncoded(), secretAlgorithm2);
// file decryption
Cipher cipher = Cipher.getInstance(algorithmEncryption);
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
FileInputStream fis = new FileInputStream(decodedB64);
FileOutputStream fos = new FileOutputStream(outputFile);
byte[] in = new byte[64];
int read;
while ((read = fis.read(in)) != -1) {
byte[] output = cipher.update(in, 0, read);
if (output != null)
fos.write(output);
}
byte[] output = cipher.doFinal();
if (output != null)
fos.write(output);
fis.close();
fos.flush();
fos.close();
System.out.println("File Decrypted.");
Oh, that one is simple. That idiotic (but funny enough, seeming largely correct otherwise) method of file encryption using CBC stores the IV in a separate file, overwriting any old one. So if you overwrite or take the wrong IV file then you'll get 16 random bytes at the start after decryption. So unless you can find the IV file that hopefully makes sense, your first 16 bytes (/characters) are now lost forever.
Of course, any sane encryption program stores the salt (a password & PBKDF2 is used for key derivation) and IV in the same file as the ciphertext.
Still, if you managed to lose the salt file or password then all the data would have been lost, so there's that...
With the added code the issue becomes even more clear. In the encryption mode you are forgetting to create & use an IvParameterSpec entirely during initialization:
cipher.init(Cipher.ENCRYPT_MODE, secret);
however, because of the way the IV data is read, you don't get any warning that the variable isn't used:
ivFis.read(iv);
If you would have created a nice method such as IvParameterSpec iv = readIvFromFile() then you would have caught this error.
Note that Java (by default in the included provider for Cipher) uses an all zero IV, so you're lucky and your data isn't partially gone.
I am given a Rijndael .Net encrypted file and .Net RSA XML Key and asked to decrypt it in Java.
The key provided to me is 256 bit.
I have parsed the RSA XML file and generated the public Key in Java. I tried to decrypt using the generated key, however I am getting the exception Illegal Key Size, I think I am doing something wrong in my Java code.
Can any one please help to check if anything is wrong with my code?
.Net encryption code:
public static void EncryptFile(string fileIn, string fileOut,
string publicKeyName, string publicKeyFile)
{
try
{
// Read the public key from key file
StreamReader sr = new StreamReader(publicKeyFile);
string strKeyText = sr.ReadToEnd();
sr.Close();
//Initialize Key container and Crypto service provider
RSACryptoServiceProvider rsa;
CspParameters cspp = new CspParameters();
cspp.KeyContainerName = publicKeyName;
rsa = new RSACryptoServiceProvider(cspp);
rsa.FromXmlString(strKeyText);
rsa.PersistKeyInCsp = true;
// Create instance of Rijndael for
// symetric encryption of the data.
RijndaelManaged alg = new RijndaelManaged();
// Key size is set to 256 for strong encryption
alg.KeySize = 256;
alg.BlockSize = 256;
// Cipher Mode is set to CBC to process the file in chunks
alg.Mode = CipherMode.CBC;
// Set padding mode to process the last block of the file
alg.Padding = PaddingMode.ISO10126;
ICryptoTransform transform = alg.CreateEncryptor();
// Use RSACryptoServiceProvider to
// enrypt the Rijndael key.
byte[] KeyEncrypted = rsa.Encrypt(alg.Key, false);
// Create byte arrays to contain
// the length values of the key and IV.
int intKeyLength = KeyEncrypted.Length;
byte[] LenK = BitConverter.GetBytes(intKeyLength);
int intIVLength = alg.IV.Length;
byte[] LenIV = BitConverter.GetBytes(intIVLength);
using (FileStream fsOut = new FileStream(fileOut, FileMode.Create))
{
// Write the following to the FileStream
// for the encrypted file (fsOut):
// - length of the key
// - length of the IV
// - ecrypted key
// - the IV
// - the encrypted cipher content
fsOut.Write(LenK, 0, 4);
fsOut.Write(LenIV, 0, 4);
fsOut.Write(KeyEncrypted, 0, intKeyLength);
fsOut.Write(alg.IV, 0, intIVLength);
// Now write the cipher text using
// a CryptoStream for encrypting.
using (CryptoStream cs = new CryptoStream(fsOut, transform, CryptoStreamMode.Write))
{
// intBlockSizeBytes can be any arbitrary size.
int intBlockSizeBytes = alg.BlockSize / 8;
byte[] DataBytes = new byte[intBlockSizeBytes];
int intBytesRead = 0;
using (FileStream fsIn = new FileStream(fileIn, FileMode.Open))
{
// By encrypting a chunk at
// a time, you can save memory
// and accommodate large files.
int intCount;
int intOffset = 0;
do
{
// if last block size is less than encryption chunk size
// use the last block size and padding character is used
// for remaining bytes
if (intBlockSizeBytes > (fsIn.Length - fsIn.Position))
{
intBlockSizeBytes = ((int)(fsIn.Length - fsIn.Position));
DataBytes = new byte[intBlockSizeBytes];
}
// read data bytes
intCount = fsIn.Read(DataBytes, 0, intBlockSizeBytes);
intOffset += intCount;
// write it into crypto stream
cs.Write(DataBytes, 0, intCount);
intBytesRead += intBlockSizeBytes;
} while (intCount > 0);
// close input file
fsIn.Close();
}
// close crypto stream
cs.FlushFinalBlock();
cs.Close();
}
// close output file
fsOut.Close();
}
}
catch
{
throw;
}
}
Java Code that I wrote to decrypt it:
byte[] expBytes = Base64.decodeBase64(pkey.getExponentEle().trim());
byte[] modBytes = Base64.decodeBase64(pkey.getModulusEle().trim());
byte[] dBytes = Base64.decodeBase64(pkey.getdEle().trim());
BigInteger modules = new BigInteger(1, modBytes);
BigInteger exponent = new BigInteger(1, expBytes);
BigInteger d = new BigInteger(1, dBytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
RSAPublicKeySpec pubSpec = new RSAPublicKeySpec(modules, exponent);
PublicKey pubKey = factory.generatePublic(pubSpec);
final byte[] keyData = Arrays.copyOf(pubKey.getEncoded(), 256
/ Byte.SIZE);
final byte[] ivBytes = Arrays.copyOf(keyData, cipher.getBlockSize());
AlgorithmParameterSpec paramSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyData, "AES"), paramSpec);
byte[] decrypted = cipher.doFinal(encrypted);
System.out.println("decrypted: " + new String(decrypted));
If I change cipher initialization to cipher.init(Cipher.DECRYPT_MODE, pubKey);, then I am getting the error Invalid AES key length: 162 bytes
You are using the public key wrong way. Do you really understad how the C# program works? what parameters is it using?
You are just using the public key bits as the AES key (even I don't realy understand how do you get 162 bytes from it).
This is example of "hybrid encryption" - the data themselves are encrypted by a random AES key (in this you claim it's 256 bit) and the AES key (in this case the IV too) is encrypted by the RSA public key. In Java there are many examples how to do that.
Even to decrypt the AES key you should know parameters used to encrypt it (RSA/ECB/PKCS5Padding, RSA-AOEP, ...), though it should be inside the XML.
Comming to the parameters - you are using PKCS5Padding, but check the .NET code, it's different
We implemented chunked file decryption in a way that the initialization vector is added at the beginning of the file and followed by the encrypted data. The following decryption method decrypts the file and write:
private void decrypt_AES_CBC_PKCS7(final byte[] symKeyBytes, final FileInputStream inputStream, final FileOutputStream outputStream) throws Exception {
Security.addProvider(new BouncyCastleProvider());
// Read init vector
final byte[] iv = new byte[16];
inputStream.read(iv, 0, 16);
// Prepare for decryption
final SecretKeySpec secretKeySpec = new SecretKeySpec(symKeyBytes, "AES");
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", new BouncyCastleProvider());
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(iv));
// Decrypt chunk by chunk
int chunkLen = 0;
final byte[] buffer = new byte[CHUNK_SIZE_DECRTPY]; // CHUNK_SIZE_DECRTPY = 20 * 1024 * 1024;
while ((chunkLen = inputStream.read(buffer)) > 0) {
byte[] decrypted = cipher.doFinal(buffer, 0, chunkLen);
outputStream.write(decrypted, 0, decrypted.length);
}
// close streams
inputStream.close();
outputStream.close();
}
The code worked fine in earlier Android versions (L & M), but when we tried it on Nexus 5X with Android N, 16 "junk" bytes were inserted at the begginning of the resulting file. This happens only in files that consist of one chunk only, i.e., files with size larger than one chunk would be decrypted correctly and no extra bytes would be prepended. Interestingly, when the code is run with Android studio debugger attached, with a breakpoint between reading IV and reading input stream, the decryption works fine and no extra bytes appear in the output file.
Example encrypted file (IV is visible as first 16 bytes, i.e., first row):
Example encrypted file
Example decrypted file, first 16 bytes are added only in Android N:
Example decrypted file
All suggestions are welcome!
As suggested by #martijno, I checked the result of inputStream.read(iv, 0, 16) and it turned out that the first invocation of it returns -1 in Android N. So, I modified the part of the code that reads the IV:
final byte[] iv = new byte[16];
int result;
int numTries = 0;
int MAX_NUM_TRIES = 2;
do {
result = inputStream.read(iv, 0, 16);
++numTries;
} while (result == -1 && numTries < MAX_NUM_TRIES);
if (result == -1) {
throw new Exception("Failed to read IV from file!");
}
It turns out that two tries for reading IV are enough, we'll test this further when more Android N devices become available.
I am developing an android application in which i need to implement some encryption.
At the same time i have to keep compatibility with other versions of application (e.g. for the WP platform), which are already in production.
This is C# code:
static public byte[] Encrypt(String passphrase, byte[] data)
{
//encrypted data
byte[] buffer = null;
//crypto handles
IntPtr hProvider = IntPtr.Zero;
IntPtr hKey = IntPtr.Zero;
try
{
if (!WinApi.CryptAcquireContext(ref hProv, null, WinApi.MS_DEF_PROV,
WinApi.PROV_RSA_FULL, WinApi.CRYPT_VERIFYCONTEXT))
Failed("CryptAcquireContext");
//128 bit hash object
if (!WinApi.CryptCreateHash(hProv,
WinApi.CALG_MD5, IntPtr.Zero, 0, ref hHash))
Failed("CryptCreateHash");
// add passphrase to hash
byte[] keyData = ASCIIEncoding.ASCII.GetBytes(passphrase);
if (!WinApi.CryptHashData(hHash, keyData, (uint)keyData.Length, 0))
Failed("CryptHashData");
// create 40 bit crypto key from passphrase hash
if (!WinApi.CryptDeriveKey(hProv, WinApi.CALG_RC2,
hHash, WinApi.CRYPT_EXPORTABLE, ref hKey))
Failed("CryptDeriveKey");
// determine how large of a buffer is required
// to hold the encrypted data
uint dataLength = (uint)data.Length;
uint bufLength = (uint)data.Length;
if (!WinApi.CryptEncrypt(hKey, IntPtr.Zero, true,
0, null, ref dataLength, bufLength))
Failed("CryptEncrypt");
// allocate and fill buffer with encrypted data
buffer = new byte[dataLength];
Buffer.BlockCopy(data, 0, buffer, 0, data.Length);
dataLength = (uint)data.Length;
bufLength = (uint)buffer.Length;
if (!WinApi.CryptEncrypt(hKey, IntPtr.Zero, true,
0, buffer, ref dataLength, bufLength))
Failed("CryptEncrypt");
}
.......
}
I have tried to implement it in Java. AFAIK, there is no default RC2 crypto provider in android, so i used Spongy Castle library (bouncycastle fork for android).
This is my Java code:
public static byte[] encryptLB(byte[] key, byte[] iv, byte[] unencrypted)
throws NoSuchAlgorithmException, ... {
MessageDigest digest = MessageDigest.getInstance("MD5");
digest.update(key);
byte[] hash = digest.digest(); //build the hash (128 bit)
Cipher cipher = Cipher.getInstance("RC2/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(hash, "RC2"));
byte[] unByte = unencrypted;
byte[] encrypted = cipher.doFinal(unencrypted);
return encrypted;
}
And the results of these functions are different.
What i am doing wrong?
How do i do it right?
Any examples and suggestions are welcome.
With best regards.
UPD The main goal is to get identical byte arrays from both functions. I can't modify c# code. First, i want to clarify am i right with c#-code:
it creates MD5 hash from the passphrase's bytes array
it generates crypto key using proprietary WinApi.CryptDeriveKey function
this key is used to encrypt data using RC2 algorithm
Second, i want to know whether there is an analogue of WinApi.CryptDeriveKey function - as i see this is the main problem.
Sorry, my question is too general, because i am not sure that the problem above (CryptDeriveKey) is the only.
Unfortunately I don't have access to a Windows machine to test this on right now but here is what I think should be interoperable.
public static byte[] encrypt(String passphrase, byte[] data) throws Exception {
// Hash the ASCII-encoded passphrase with md5
byte[] keyData = passphrase.getBytes(Charset.forName("US-ASCII"));
MessageDigest md = MessageDigest.getInstance("MD5");
byte [] md5HashOfKey = md.digest(keyData);
// Need to use bouncycastle (spongycastle on Android) to get RC2
Security.addProvider(new BouncyCastleProvider());
Cipher rc2 = Cipher.getInstance("RC2/CBC/PKCS5PADDING");
// Create an RC2 40-bit key from the 1st 5 bytes of the hash.
SecretKeySpec rc2KeySpec = new SecretKeySpec(md5HashOfKey, 0, 5, "RC2");
rc2.init(Cipher.ENCRYPT_MODE, rc2KeySpec);
byte [] cipher = rc2.doFinal(data);
return cipher;
}
I have an encrypted mp4 using Rijndael and I am decrypting in C# in the following manner.
System.Security.Cryptography.Rijndael crypt = System.Security.Cryptography.Rijndael.Create();
crypt.Key = convertedSecureString;
byte[] initializationVectorLength = new byte[sizeof(int)];
CryptoStream cryptostream = new CryptoStream(inputStream, crypt.CreateDecryptor(), CryptoStreamMode.Read);
byte[] buffer = new byte[1024];
int len;
while ((len = cryptostream.Read(buffer, 0, buffer.Length)) > 0)
{
outputStream.Write(buffer, 0, len);
buffer = new byte[1024];
}
outputStream.Flush();
cryptostream.Close();
inputStream.Close();
Now I need to convert this code to Java/Android equivalent. I am not sure where to start frankly. I am confused by so many options - some say use Bouncy Castle, some say Apache Commons, some the native Java lib. How do I do this. And what do I do about CryptoStream etc?
UPDATE
I am using the following the code in C# for assigning the key
byte[] convertedSecureString = new byte[this.key.Length];
IntPtr ptr = System.Runtime.InteropServices.Marshal.SecureStringToBSTR(this.key);
for (int i = 0, j = 0; i < this.key.Length * UnicodeByteLength; i = i + UnicodeByteLength, j++)
{
convertedSecureString[j] = System.Runtime.InteropServices.Marshal.ReadByte(ptr, i);
}
try
{
crypt.Key = convertedSecureString;
}
where key is secure. I have the equivalent unsecure key in Java. How do i convert this piece of code to Java
UPDATE
Rfc2898DeriveBytes newKey = new Rfc2898DeriveBytes(crypt.Key.ToString(), crypt.IV);
Array.Copy(newKey.GetBytes((int)crypt.KeySize / 8), crypt.Key, (int)crypt.KeySize / 8);
Array.Copy(newKey.GetBytes((int)crypt.BlockSize / 8), crypt.IV, (int)crypt.BlockSize / 8);
I am using this in C# to mod the key using Rfc 2898 and derive the bytes - I cant find an equivalent in Java - I found here Java equivalent of C#'s Rfc2898DerivedBytes in the second comment - but what values do I give for iterator and dklen?
You need to get Cipher object. Here is one way of getting it, using byte[] aesKey, and byte[] iv (initialization vector, must always be 16 bytes for AES).
// aesKey is 128, 196 or 256-bit key (8, 12 or 16 byte array)
SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
// initialization vector
IvParameterSpec ivSpec = new IvParameterSpec(iv);
// create and initialize cipher object
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
Once you have Cipher object in decrypt mode, you can feed it with encrypted data using update methods, and it will return you plain-text data. When you are done, you must call one of doFinal methods to get final block.
Alternatively, you can create CipherInputStream using your Cipher object, and original input stream that supplies encrypted data. You read data from CipherInputStream, which in turn reads data from original input stream, decrypts it, and returns you the plain-text data.
For encrypting, you need to pass Cipher.ENCRYPT_MODE into Cipher.init method, and use CipherOutputStream instead.
Update: full example, more or less equivalent to original C# code:
// Algorithm, mode and padding must match encryption.
// Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
// If you have Bouncycastle library installed, you can use
// Rijndael/CBC/PKCS7PADDING directly.
Cipher cipher = Cipher.getInstance("Rijndael/CBC/PKCS7PADDING");
// convertedSecureString and initVector must be byte[] with correct length
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(convertedSecureString, "AES"),
new IvParameterSpec(initVector));
CipherInputStream cryptoStream = new CipherInputStream(inputStream, cipher);
byte[] buffer = new byte[1024];
int len = cryptoStream.read(buffer, 0, buffer.length);
while (len > 0) {
outputStream.write(buffer, 0, len);
len = cryptoStream.read(buffer, 0, buffer.length);
}
outputStream.flush();
cryptoStream.close();
// no need to close inputStream here, as cryptoStream will close it
By default, Java doesn't support Rijndael algorithm (AES may or may not work, see the other answer) and PKCS7 padding. You will need to install Bouncycastle extension for that and then just use Cipher.getInstance("Rijndael/CBC/PKCS7PADDING");. Why CBC and PKCS7 Padding? Those seem to be defaults for System.Security.Cryptography.Rijndael class. If I got that wrong, use correct mode and padding for your situation.
Don't worry about the implementation in C#. Go through the below link for encrypting/decrypting for Java Language.RjIndeal Implementation in Java