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
Related
I have below code to encrypt some file content in java by using AES/CTR/NOPADDING mode. I am using crypto package of javax. Also I am using same secret key to generate key and iv.
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
byte[] secretKey = Base64.decodeBase64("uQsaW+WMUrjcsq1HMf+2JQ==");
SecretKeySpec key = new SecretKeySpec(secretKey, "AES");
IvParameterSpec iv = new IvParameterSpec(secretKey);
cipher.init(mode, key , iv);
FileInputStream fileInputStream = new FileInputStream(sourceFilePath);
FileOutputStream fileOutputStream = new FileOutputStream(destFilePath);
int read = 0;
while ((fileInputStream.available()) > 0) {
byte[] block = new byte[4096];
read = fileInputStream.read(block);
byte[] writeBuffer = cipher.update(block);
fileOutputStream.write(writeBuffer, 0, read);
}
byte[] writeBuffer = cipher.doFinal();
fileOutputStream.write(writeBuffer, 0, writeBuffer.length);
fileInputStream.close();
fileOutputStream.close();
I am not able to decrypt encrypted content in javascript by using cryptojs.
Here is something I have tried.
var key = CryptoJS.enc.Hex.parse(atob('uQsaW+WMUrjcsq1HMf+2JQ=='));
var decrypted = CryptoJS.AES.decrypt(encryptedContent, key, {
mode: CryptoJS.mode.CTR,
iv: key,
padding: CryptoJS.pad.NoPadding
});
var decryptedText = CryptoJS.enc.Utf8.stringify(decrypted);
Can somebody tell me what I am doing wrong? Or tell me how to do it.
I am able to encrypt and decrypt in java and javascript independently.
In the CryptoJS-documentation is explained which data types and parameters the CryptoJS.decrypt()-method expects and which encoders are available:
The key has to be passed to the CryptoJS.decrypt()-method as a WordArray. Since the key-data are Base64-encoded, they can be converted into a WordArray with the CryptoJS.enc.Base64.parse()-method.
The ciphertext can be passed to the CryptoJS.decrypt()-method as a WordArray inside a CipherParams-object. The Java-code stores the encrypted data in a file. Assuming that the string encryptedContent contains those data as hex-string (unfortunately, this does not emerge from the posted code, so that an assumption must be made here), they can be converted into a WordArray with the CryptoJS.enc.Hex.parse()-method and wrapped in a CipherParams-object.
The CryptoJS.decrypt()-method returns a WordArray which can be converted with the CryptoJS.enc.Utf8.stringify()-method into a string.
If the following plain text is contained in the input-file:
This is the plain text which needs to be encrypted!
the Java-code stores the following byte-sequence (= encrypted data) in the output-file:
52F415AB673427C42278E8D6F34C16134D7E3FE7986500980ED4063F3CF51162592CE0F5412CCA0BC2DBAE3F2AEC2D585EE8D7
The JavaScript-Code for the decryption is:
var key = CryptoJS.enc.Base64.parse('uQsaW+WMUrjcsq1HMf+2JQ==');
var encryptedContent = '52F415AB673427C42278E8D6F34C16134D7E3FE7986500980ED4063F3CF51162592CE0F5412CCA0BC2DBAE3F2AEC2D585EE8D7';
var cipherParams = CryptoJS.lib.CipherParams.create({
ciphertext: CryptoJS.enc.Hex.parse(encryptedContent)
});
var decrypted = CryptoJS.AES.decrypt(cipherParams, key, {
mode: CryptoJS.mode.CTR,
iv: key,
padding: CryptoJS.pad.NoPadding
});
var decryptedText = CryptoJS.enc.Utf8.stringify(decrypted);
console.log(decryptedText);
which displays the original plain text in the console. To run the code above at least CryptoJS-version 3.1.4 is needed (see versions, cdnjs).
Your encryption loop is wrong. I am not sure if it's the cause of your issues, but I'd start with it
read = fileInputStream.read(block);
byte[] writeBuffer = cipher.update(block);
even if you read only partial size of the block, you execute encryption operation over the whole block, you may try
byte[] writeBuffer = cipher.update(block, 0, read);
About using the key as IV, I have to stress that with CTR mode the security will be completely broken.
I'm try to be compatible Encrypt/Decrypt both C# and Java.
As I know the default mode is 'ecb/pkcs5' in Java, and 'cbc/pkcs7' in C#.
So I match these things.
1st question is that PKCS7 and PKCS5 are compatible each other??,
there is no PKCS7 in Java so I use PKCS5. but I can get same encrypted data [even the padding-way is different ,pkcs7/pkcs5,] Is it possible? or these are compatible?
2nd question is that Why I get same result even though the mode, way are all different?
I compare 'DES-ECB / DES-CBC / TripleDES-ECB' these things. and C# is working well, results are all different.
Input > HELLO Output > (ECB)/dZf3gUY150= (CBC) V17s5QLzynM= (Triple)sWGS0GMe1jE
but I get same reulst in Java ..
Input > HELLO Output > (ECB)/dZf3gUY150= (CBC)/dZf3gUY150= (Triple)/dZf3gUY150=
When debugging the flow is right.
Here is my code.
C#
public static string Encrypt_DES(string originalString, byte[] key, string mode)
{
DESCryptoServiceProvider cryptoProvider = new DESCryptoServiceProvider();
if (mode.Equals("ECB"))
cryptoProvider.Mode = CipherMode.ECB;
else if (mode.Equals("CBC"))
{
cryptoProvider.Mode = CipherMode.CBC;
cryptoProvider.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 };
}
cryptoProvider.Padding = PaddingMode.PKCS7;
MemoryStream memoryStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoProvider.CreateEncryptor(key, key), CryptoStreamMode.Write);
StreamWriter writer = new StreamWriter(cryptoStream);
writer.Write(originalString);
writer.Flush();
cryptoStream.FlushFinalBlock();
writer.Flush();
return Convert.ToBase64String(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
}
public static string Encrypt_TripleDES(string source, string key)
{
TripleDESCryptoServiceProvider desCryptoProvider = new TripleDESCryptoServiceProvider();
MD5CryptoServiceProvider hashMD5Provider = new MD5CryptoServiceProvider();
byte[] byteHash;
byte[] byteBuff;
byteHash = hashMD5Provider.ComputeHash(Encoding.UTF8.GetBytes(key));
desCryptoProvider.Key = byteHash;
desCryptoProvider.Mode = CipherMode.ECB; //CBC, CFB
desCryptoProvider.Padding = PaddingMode.PKCS7;
byteBuff = Encoding.UTF8.GetBytes(source);
string encoded = Convert.ToBase64String(desCryptoProvider.CreateEncryptor().TransformFinalBlock(byteBuff, 0, byteBuff.Length));
return encoded;
}
Java(Android)
public String Encrypt(String str, String desKey, String mode) {
try {
KeySpec keySpec = null;
SecretKey key = null;
Cipher ecipher = null;
if (desKey.length() == 8) {
keySpec = new DESKeySpec(desKey.getBytes("UTF8"));
key = SecretKeyFactory.getInstance("DES").generateSecret(keySpec);
if(mode.equals(ECB)){
ecipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
ecipher.init(Cipher.ENCRYPT_MODE, key);
}else if (mode.equals(CBC)){
ecipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes);
ecipher.init(Cipher.ENCRYPT_MODE, key,ivSpec);
}
} else if (desKey.length() == 24) {
keySpec = new DESedeKeySpec(desKey.getBytes("UTF8"));
key = SecretKeyFactory.getInstance("DESede").generateSecret(keySpec);
ecipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
ecipher.init(Cipher.ENCRYPT_MODE, key);
}
byte[] data = str.getBytes("UTF-8");
byte[] crypt = ecipher.doFinal(data);
return Base64.encodeToString(crypt, 0);
} catch (Exception ex) {
Log.d("ZVM", ex.getMessage());
}
return null;
}
As I understand 'IV' is for CBC, When making password, it is mixed with IV(not the key but like key). Is it right?
Thanks.
PKCS7 and PKCS5 are compatible each other
PKCS#5 and PKCS#7 paddings are compatible (equal) for DES. For AES, Java actually uses PKCS#7 padding even though you would write AES/xyz/PKCS5Padding.
Why I get same result even though the mode, way are all different?
First, let's see how Java behaves. The ciphertexts for DES-ECB, DES-CBC and DESede-ECB are all equal. This is correct if
the key is the same (DES supports only 8 byte keys, but Triple DES supports 8, 16 and 24 byte keys where non-24 byte keys are expanded to 24 byte keys),
the plaintext is the same,
the plaintext is less than 8 bytes long (block size of DES/Triple DES) and
the IV is an all 0x00 bytes IV.
Those are all true in the Java code. If you have trouble grasping that, combine the encryption routines for the ECB and CBC modes of operation.
The result of Triple DES might be a bit confusing. I assume that you've taken your 8 byte key for DES and replicated it either twice or thrice for use in Triple DES. This is an issue, because Triple DES encryption consists of three steps of normal DES: EDE means Encryption + Decryption + Encryption. If all the three subkeys are the same, the one of the Encryption steps cancels out with the Decryption step and the whole thing is equivalent to a single DES encryption.
Let's see why C# behaves differently:
The ciphertext from DES-CBC is different from DES-ECB, because the IV is not an all 0x00 bytes IV. cryptoProvider.CreateEncryptor(key, key) creates an Encryptor with the IV set to key (the second argument). That's not what you want. Just use cryptoProvider.CreateEncryptor() instead.
The ciphertext from DESede-ECB is different from DES-ECB, because you're running the key through a hash function. The key is therefore different.
Don't use DES nowadays. It only provides 56 bit of security. AES would be a much better, because it's more secure with the lowest key size of 128 bit. There is also a practical limit on the maximum ciphertext size with DES. See Security comparison of 3DES and AES.
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 for a few days reading this and other tutorials about javax.crypto
Using block modes and initialisation vectors in Java
My test code below is a Client who send data to a Server.
I read about different block modes and the CFB8 stream mode seem to be working because i split arbitrary size files up in chunks. Every chunk is 0.5MB except for the last chunk that are smaller, they are sent one after another to the Server that put the file back together.
I have a few questions:
1) Should i use Asymmetric cryptography publ/priv keys to send the SecretKeySpec password and the IV to the server before i start the transfer?
2) What are the SecretKeySpec password used for, protect the IV?
THE CLIENT encrypt data
Cipher cipher = Cipher.getInstance("AES/CFB8/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec("password12345678".getBytes(), "AES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
out.write(iv); //Send IV to Server
out.flush();
// THE ENCRYPTET STREAM
cos = new CipherOutputStream(out, cipher);
while ((val = byteArrayInputStream.read(buffer, 0, 1024)) > 0) {
cos.write(buffer, 0, val);
cos.flush();
}
cipher.doFinal()
THE SERVER decrypt data
byte[] iv = new byte[16];
in.read(iv);
Cipher cipher = Cipher.getInstance("AES/CFB8/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec("password12345678".getBytes(), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
cos = new CipherInputStream(in, cipher);
while (offset < tmpBuffer.length && (numRead=cos.read(tmpBuffer, offset, tmpBuffer.length-offset)) >= 0) {
offset += numRead;
savedFileSize = savedFileSize + numRead;
}
// CREATE HASH FROM THE DOWNLOAD CHUNK PART
String retCrC = DoEncryption.getCRC32ChecksumFromArray(tmpBuffer);
String hash2 = Long.toHexString( Long.parseLong(retCrC) );
// TEST SO THE REMOTE HASH MATCH THE LOCAL HASH
if(!hash1.equals(hash2)){
...
Should I use asymmetric cryptography publ/priv keys to send the SecretKeySpec password and the IV to the server before I start the transfer?
This is broadly how SSL works, albeit usually in reverse. Any reason you couldn't use SSL/TLS instead? Generating your own secure transfer protocol is not a trivial undertaking.
Usually one protects the IV by including it into the HMAC.
To protect my simple code in the question one can use SSL/TSL.
Got everything working using SSL/TSL with self-signed certificates from the blog here:
#Marcus Krantz's blog Creating iself-signed certificates
I must say this is not for the faint hearted..:)
BTW Is it true that only the bcprov-jdk15on-146 works and not the bcprov-jdk15on-147 when using the keytool for the *.bks files?
I am in the process of implementing a Java library in Ruby. I have come across the following road block. Is it possible to implement the following code in ruby? Are there any ruby equivalents for byte[], IvParameterSpec, SecretKeySpec ?
private String decrypt(String token)
{
//parse token into its IV and token components
byte[] ivAndToken = Base64.decodeBase64(token);
byte[] iv = new byte[ivLength];
System.arraycopy(ivAndToken, 0, iv, 0, ivLength);
int length = ivAndToken.length - ivLength;
byte[] tokenBytes = new byte[length];
System.arraycopy(ivAndToken, ivLength, tokenBytes, 0, length);
//prepare initialization vector specification
IvParameterSpec spec = new IvParameterSpec(iv);
//create cipher instance based on transformer params
Cipher cipher = Cipher.getInstance(algorithm + mode + padding, CRYPTO_PROVIDER);
//convert key bytes into valid key format
Key key = new SecretKeySpec(Base64.decodeBase64(symkey), algorithm);
//initialize cipher for decryption
cipher.init(Cipher.DECRYPT_MODE, key, spec);
//decrypt the payload
String plaintext = new String(cipher.doFinal(tokenBytes));
return plaintext;
}
You'll probably have to implement both IvParameterSpec and SecretKeySpec on Ruby if you want the algorithm to behave exactly like it does in Java. byte[] is of course just a byte array. You'll probably want to at the docs for them (links above) and also hopefully you understand block cipher operation modes work.
If you don't, SecretKey refers to the symmetric key (eg: the passphrase), and IV is the initialization vector, a cryptographic nonce used to make different encryptions of the same plaintext generate different ciphertext. IV's are needed for all operation modes except ECB. See this wikipedia page for more details.