I have a problem decrypting an encrypted string that was encrypted in Java using the DES algorithm. I think my main problem is, that I don't see any salt or IV specifications in the java code.
I have following information:
This HexSequence is the encrypted data I have to decrypt: 9465E19A6B9060D75C3F7256ED1F4D21EDC18BB185304B92061308A32725BE760F1847E3B19C1D3548F61165EA2E785E48F61165EA2E78
Algorithm: DES, Padding: DES/ECB/NoPadding, Key: TESTKEY123
After decryption I should get: 550000000018h000000273Al2011112214340600000000000000000000000000
The java-code used to encrypt the data looks like this:
public class Encryptor {
private SecretKey secretKey;
private Cipher cipher;
public Encryptor(String algorithmName, String paddingName, String key) {
String keyHexCode = StringUtils.convertUnicodeToHexCode(key.getBytes());
try {
byte[] desKeyData = StringUtils.convertHexStringToByteArray(keyHexCode);
DESKeySpec desKeySpec = null;
try {
desKeySpec = new DESKeySpec(desKeyData);
} catch (InvalidKeyException e) {
e.printStackTrace();
}
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithmName);
try {
secretKey = keyFactory.generateSecret(desKeySpec);
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
try {
cipher = Cipher.getInstance(paddingName);
} catch (NoSuchPaddingException e) {
// TODO: handle exception
}
} catch (NoSuchAlgorithmException e) {
// TODO: handle exception
}
}
private void initEncryptor(int mode) {
try {
cipher.init(mode, secretKey);
} catch (InvalidKeyException e) {
// TODO: handle exception
}
}
public String encrypt(String clearText) {
initEncryptor(Cipher.ENCRYPT_MODE);
try {
// Encrypt the cleartext
byte[] encryptedBytes = cipher.doFinal(clearText.getBytes());
return StringUtils.convertUnicodeToHexCode(encryptedBytes).toUpperCase();
} catch (IllegalBlockSizeException e) {
// TODO: handle exception
} catch (BadPaddingException e) {
// TODO: handle exception
}
return "";
}
public String decrypt(String encryptedTextHex) {
byte[] encryptedText = StringUtils.convertHexCodeSequenceToUnicode(encryptedTextHex);
initEncryptor(Cipher.DECRYPT_MODE);
try {
// Decrypt the encryptedTextHex
return new String(cipher.doFinal(encryptedText));
} catch (IllegalBlockSizeException e) {
// TODO: handle exception
} catch (BadPaddingException e) {
// TODO: handle exception
}
return "";
}
}
I tried to use following .net-code to decrypt the data:
public class URLDecryptor
{
public static string GetValue(string Data)
{
DESCryptoServiceProvider cryptoProvider = new DESCryptoServiceProvider();
byte[] bytes = System.Text.UnicodeEncoding.Unicode.GetBytes("TESTKEY123");
byte[] salt = new byte[8];
byte[] iv = new byte[8];
Rfc2898DeriveBytes password = new Rfc2898DeriveBytes("TESTKEY123", salt);
cryptoProvider.Key = password.GetBytes(8);
cryptoProvider.IV = iv;
cryptoProvider.Padding = PaddingMode.None;
cryptoProvider.Mode = CipherMode.ECB;
MemoryStream memStream = new MemoryStream(convertHexCodeSequenceToUnicode(Data));
CryptoStream cryptoStream = new CryptoStream(memStream, cryptoProvider.CreateDecryptor(cryptoProvider.Key, cryptoProvider.IV), CryptoStreamMode.Read);
StreamReader reader = new StreamReader(cryptoStream);
string value = reader.ReadToEnd;
reader.Close();
cryptoStream.Close();
return value;
}
private static byte[] convertHexCodeSequenceToUnicode(string hexCodeSequence)
{
byte[] bytes = new byte[(hexCodeSequence.Length / 2) + 1]; //This is strange
int index = 0;
int count = 0;
while (count < hexCodeSequence.Length) {
string hexCode = hexCodeSequence.Substring(count, 2);
bytes[index] = getHexValue(hexCode);
count += 2;
index += 1;
}
return bytes;
}
public static byte getHexValue(string hexCode)
{
return byte.Parse(hexCode, System.Globalization.NumberStyles.HexNumber);
}
}
What's strange is that line:
byte[] bytes = new byte[(hexCodeSequence.Length / 2) + 1];
The data is 55 bytes long but I have to put it in 56 bytes. It appends a 0-byte to the and of the array, but if I don't do this the cryptostream throws an error that the data to decrypt is too short.
If I try it this way I only get garbage as output. I'm using a empty salt and IV because I can't see which salt and IV the java code is using. Are there any default values I don't know?
EDIT:
Java code to get the byte out of the hexCode:
private static byte getNegativeValueForHexConversion(String hexCode) {
int i = Integer.parseInt(hexCode, 16);
return (byte) (i > 127 ? i - 256 : i);
}
Looks like Java uses a signed byte and .Net uses an unsigned byte for all its functions. Is this maybe the problem?
DES is a block cipher with a 64-bit block size. Thus (in ECB mode at least) the ciphertext you have to decrypt must be a multiple of 64 bits (8 bytes) long. Yours is 55 bytes, so you do not have the full ciphertext - this is why you're having to add a zero byte. Have you run the Java code yourself and seen that the output is 55 bytes long? Is this a copy and paste error?
The exception to this would be DES used in a mode which effectively creates a key stream, that is then XORed with the plaintext to produce the ciphertext. This would include CFB, OFB and CTR modes. So one possibility is that decrypting with one of these would work (off the top of my head, I can't remember whether the .NET crypto libraries support CTR). Are you sure that ECB was specified in the Java code?
But also, you have the problem that the Java code looks like it's doing a straightforward text to hex conversion from the key text to get the key bytes, whereas the .NET code is doing an RFC-2898 compatible conversion, which will not give you the same key bytes.
Related
Encryption :
public static byte[] EncryptAES(Message msg)
{
byte[] encText; // This will keep the encrypted text
byte[] encLength; // This will keep the length of the encrypted text
byte[] finalEncText = null; // This keeps the encLength + encText (#####[encText] / [encLength][encText])
// Building the plaintext message :
string plainText = msg.MessageCode.ToString();
if (msg.Parameters != null)
foreach (string parameter in msg.Parameters)
plainText += parameter;
// Encrypting the plaintext :
encText = EncryptAES(plainText);
string encLen = encText.Length.ToString();
string fittedEncLen = MessageSender.FitStringIntoSize(encLen, Globals.MESSAGE_LENGTH_LEN); // Fit the length of the encrypted text into a certain size
encLength = Encoding.ASCII.GetBytes(fittedEncLen); // convert the length into byte[]
finalEncText = new byte[encLength.Length + encText.Length];
System.Buffer.BlockCopy(encLength, 0, finalEncText, 0, encLength.Length);
System.Buffer.BlockCopy(encText, 0, finalEncText, encLength.Length, encText.Length); // Copy the byte arrays into the new byte array
return finalEncText;
}
private static byte[] EncryptAES(string text)
{
// This function encrypts a plaintext message using the aes key we have from the server
if (AesKey == null || IV == null) // If we dont have an aes key / iv, dont encrypt
return Encoding.ASCII.GetBytes(text);
byte[] encryptedText;
try
{
Aes aes = Aes.Create();
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.Zeros;
aes.Key = Encoding.ASCII.GetBytes(AesKey);
aes.IV = Encoding.ASCII.GetBytes(IV);
ICryptoTransform cryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using (MemoryStream memStream = new MemoryStream())
{
using (CryptoStream crypotStream = new CryptoStream(memStream, cryptor, CryptoStreamMode.Write))
{
using (StreamWriter writerStream = new StreamWriter(crypotStream))
{
writerStream.Write(text);
}
encryptedText = memStream.ToArray();
}
}
aes.Dispose();
}
catch
{
// In case of an error while encrypting, dont encrypt
encryptedText = Encoding.ASCII.GetBytes(text);
}
return encryptedText;
}
[The added fittedEncLen is basically a prefix of fixed length of 5 chars, that contains the length of the encrypted message following it, before decrypting the server reads those 5 chars and then it decrypts the encrypted part]
Sending the message to the server [TCPClient] [C#] :
public int Send(Message message)
{
/*
* Encrpyts the message and then sends it to the network stream.
*
* Return code:
* 0 on success.
* -1 on failure.
*/
byte[] msg = Cryptography.EncryptAES(message); // Encrypt the message
// Sending message
try
{
this._networkStream.Write(msg, 0, msg.Length);
this._networkStream.Flush();
}
catch
{
return -1;
}
return 0;
}
Receiving [C++] :
wstring Helper::getWideStringPartFromSocket(SOCKET sc, int bytesNum)
{
// This function reads the message from the socket, using wide string
std::wstringstream cls;
cls << getPartFromSocket(sc, bytesNum, 0);
return cls.str();
}
char* Helper::getPartFromSocket(SOCKET sc, int bytesNum, int flags)
{
if (bytesNum == 0)
return "";
char* data = new char[bytesNum + 1];
int res = recv(sc, data, bytesNum, flags);
if (res == INVALID_SOCKET)
{
string s = "Error while recieving from socket: ";
s += to_string(sc);
throw exception(s.c_str());
}
data[bytesNum] = 0;
return data;
}
BufferedString* Helper::makeBufferedString(SOCKET sc)
{
/*
The socket contains <length of encrypted message (unencrypted)> <encrypted message>.
This function will read the length of the unencrypted message, read
the encrypted message, decrypt it, store it in a BufferedString
object and return the object.
Length of length number: MESSAGE_LENGTH_LEN.
*/
int sizeOfMessage = Helper::getIntPartFromSocket(sc, MESSAGE_LENGTH_LEN);
if (sizeOfMessage == 0)
return NULL;
wstring wideString = getWideStringPartFromSocket(sc, sizeOfMessage);
string decrypted = "";
if (wideString.length() < sizeOfMessage)
{
std::wstringstream cls;
cls << wideString;
cls << getWideStringPartFromSocket(sc, sizeOfMessage - wideString.length());
wideString = cls.str();
}
SocketEncryptionKeychain* keyChain = SocketEncryptionKeychain::getKeychain(sc);
if (keyChain != nullptr) // If the socket has a keychain, decrypt the message
decrypted = Cryptography::decryptAES(wideString, keyChain->getKey(), keyChain->getIV()); // Try to decrypt the message
else // If the keychain is null, just convert the widestring to a string
decrypted = wideStringToString(wideString);
return new BufferedString(decrypted);
}
SocketEncryptionKeychain basically contains the AES Key and IV for each socket
BufferedString is a class that contains the string, and you can read from it like you read from a socket [its a buffer that once you read from it, what you read is deleted] [basically a string buffer, nothing special]
Decrypting [C++]:
string Cryptography::decryptAES(wstring cipherText, byte aesKey[], byte iv[])
{
if (aesKey == nullptr || iv == nullptr) // If the key or iv are null, dont decrypt
return Helper::wideStringToString(cipherText);
string plaintext;
try
{
// Decrypt :
byte* cipher = wideStringToByteArray(cipherText); // Convert the wide string to byte*
CryptoPP::AES::Decryption aesDecryption(aesKey, 32);
CryptoPP::CBC_Mode_ExternalCipher::Decryption ecbDecryption(aesDecryption, iv);
CryptoPP::StreamTransformationFilter stfDecryptor(ecbDecryption, new CryptoPP::StringSink(plaintext), StreamTransformationFilter::ZEROS_PADDING);
stfDecryptor.Put(cipher, cipherText.length());
stfDecryptor.MessageEnd();
Helper::safeDelete(cipher);
}
catch (CryptoPP::InvalidCiphertext& ex)
{
// In case of an error don't decrypt
plaintext = Helper::wideStringToString(cipherText);
}
return plaintext;
}
byte* Cryptography::wideStringToByteArray(wstring text)
{
// This function translates the wstring into a byte*
byte* bytes = new byte[text.length()]; // Convert the wstring to byte*
for (int i = 0; i < text.length(); i++)
{
bytes[i] = text[i];
}
return bytes;
}
[Helper::safeDelete is a function that just deletes the pointer and sets it as null]
The decryption only fails once in a while
You may have other problems, but here's one:
using (CryptoStream crypotStream = new CryptoStream(memStream, cryptor, CryptoStreamMode.Write))
{
using (StreamWriter writerStream = new StreamWriter(crypotStream))
{
writerStream.Write(text);
}
encryptedText = memStream.ToArray();
}
You drained the CryptoStream's output before telling the CryptoStream it was done. So you've possibly lost up to 16 bytes.
You need to either:
call FlushFinalBlock() on crypotStream(sic).
don't call memStream.ToArray() until after the using for the CryptoStream has exited.
So, the problem was while parsing the char* to wstring in the function
The problem in this function is the way I parse it:
wstring Helper::getWideStringPartFromSocket(SOCKET sc, int bytesNum)
{
// This function reads the message from the socket, using wide string
std::wstringstream cls;
cls << getPartFromSocket(sc, bytesNum, 0);
return cls.str();
}
I used a wstringstream, and the encrypted text can sometimes contain null-terminating character.
So instead of using a wstringstream I used this:
wstring Helper::getWideStringPartFromSocket(SOCKET sc, int bytesNum)
{
// This function reads the message from the socket, using wide string
char* readBuffer = getPartFromSocket(sc, bytesNum, 0);
return wstring(&readBuffer[0], &readBuffer[bytesNum]);
}
and then it does not cut the message at null-character
I am working on re-writing our encryption class to be FIPS compliant, and in doing so have to re-work how we're handling non-secret payload data. At the moment, I'm writing out the size of my non-secret payload, then writing the size of my IV. I follow that up by writing my non-secret payload and IV, with all of these writes sharing a BinaryWriter. Lastly, I then share the same MemoryStream and write my the data needing to be encrypted into the the CryptoStream.
This is what the class currently looks like:
public class Encryption
{
private const int SaltBlockSize = 8;
private const int SaltBitSize = 64;
private const int KeyBitSize = 256;
private const int SaltIterations = 10000;
private const int nonSecretPayloadOffsetInPayload = 0;
private const int ivOffsetInPayload = 1;
public byte[] GetNonSecretPayload(byte[] completePayload)
{
byte[] nonSecretPayload;
using (var memoryStream = new MemoryStream(completePayload))
{
using (var binaryReader = new BinaryReader(memoryStream))
{
int nonSecretPayloadLength = binaryReader.ReadInt32();
binaryReader.BaseStream.Position = 3;
nonSecretPayload = binaryReader.ReadBytes(nonSecretPayloadLength);
}
}
return nonSecretPayload;
}
public byte[] EncryptMessageWithPassword(byte[] secretMessage, string password, byte[] nonSecretPayload = null)
{
if (string.IsNullOrEmpty(password))
{
throw new InvalidOperationException("You can not provide an empty password, you must give a string that is at least 12 characters in size. If you just want to obfuscate the message without any protection, an alternative way is to use a Base64 String");
}
else if (password.Length < 12)
{
throw new InvalidOperationException("The minimum size your password can be is 12 characters.");
}
byte[] saltHash;
byte[] saltKey = this.CreateSaltKeysFromPassword(password, 0, out saltHash);
byte[] encryptedValue = null;
using (AesCryptoServiceProvider aesProvider = new AesCryptoServiceProvider())
{
aesProvider.Key = saltKey;
aesProvider.Mode = CipherMode.CBC;
aesProvider.Padding = PaddingMode.PKCS7;
aesProvider.GenerateIV();
using (MemoryStream memoryStream = new MemoryStream())
{
// Write our IV out first so we can pull the IV off later during decryption.
// The IV does not need to be encrypted, it is safe to store as as unencrypted buffer in the encrypted byte array.
using (BinaryWriter ivWriter = new BinaryWriter(memoryStream, Encoding.UTF8, true))
{
// The first two writes to the stream should be the size of the non-secret payload
// and the size of the IV. If no payload exists, then we write 0.
if (nonSecretPayload == null || nonSecretPayload.Length == 0)
{
ivWriter.Write(0);
}
else
{
ivWriter.Write(nonSecretPayload.Length);
}
ivWriter.Write(aesProvider.IV.Length);
// If we have a payload, write it out.
if (nonSecretPayload != null && nonSecretPayload.Length > 0)
{
ivWriter.Write(nonSecretPayload);
}
// Write the Initialization Vector.
ivWriter.Write(aesProvider.IV);
}
// Create our encryptor and write the secret message to the encryptor stream.
var encryptor = aesProvider.CreateEncryptor(saltKey, aesProvider.IV);
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(secretMessage, 0, secretMessage.Length);
cryptoStream.FlushFinalBlock();
}
// Get the non-secret payload, IV, payload and IV lengths and encrypted data back as an array of bytes.
encryptedValue = memoryStream.ToArray();
}
}
return encryptedValue;
}
public string EncryptMessageWithPassword(string secretMessage, string password, byte[] nonSecretPayLoad = null)
{
byte[] secreteMessageBytes = Encoding.UTF8.GetBytes(secretMessage);
byte[] encryptedMessage = this.EncryptMessageWithPassword(secreteMessageBytes, password, nonSecretPayLoad);
return Convert.ToBase64String(encryptedMessage);
}
private byte[] CreateSaltKeysFromPassword(string password, int nonSecretPayloadSize, out byte[] saltHash)
{
byte[] saltKey;
//Use Random Salt to prevent pre-generated weak password attacks.
using (var generator = new Rfc2898DeriveBytes(password, SaltBitSize / SaltBlockSize, SaltIterations))
{
// Get a generated salt derived from the user password, hashed n-times where n = SaltIterations
saltHash = generator.Salt;
//Generate Keys
saltKey = generator.GetBytes(KeyBitSize / SaltBlockSize);
}
return saltKey;
}
}
I would expect in my GetNonSecretPayload(byte[] payload); that by setting the position, or using binaryReader.BaseStream.Seek(2); to skip the IV length item, I would skip the IV size entry in the byte[] array and be able to read the bytes associated with the actual non-secret data. This doesn't work though, presumably because this isn't an array underneath the covers that I can just move to the next element in the array, skipping the IV length wrote out originally.
I have the following unit test.
[TestClass]
public class EncryptionTests
{
private const string _ContentToEncrypt = "This is a test to make sure the encryption Type actually encrypts the data right.";
private const string _Password = "EncryptedPassword1";
[TestMethod]
public void Extract_non_secret_payload_content_from_encrypted_string()
{
// Arrange
var encryption = new Encryption();
string nonSecretData = "My payload is not considered secret and can be pulled out of the payload without decrypting";
// Convert the secret and non-secret data into a byte array
byte[] payload = Encoding.UTF8.GetBytes(nonSecretData);
byte[] encodedBytes = Encoding.UTF8.GetBytes(_ContentToEncrypt);
// Encrypt the secret data while injecting the nonsecret payload into the encrypted stream.
byte[] encryptedValue = encryption.EncryptMessageWithPassword(encodedBytes, _Password, payload);
// Act
// Pull the non-secret payload out of the encrypted message - without having to decrypt it.
byte[] UnencryptedPayloadWithinEncryptedArray = encryption.GetNonSecretPayload(encryptedValue);
string payloadContent = Encoding.UTF8.GetString(UnencryptedPayloadWithinEncryptedArray);
// Assert
Assert.AreEqual(nonSecretData, payloadContent);
}
}
What I get with my current binaryReader.BaseStream.Position = 3 is
"\0\u0010\0\0\0My payload is not considered secret and can be pulled out of the payload without decry"
I've read and wrote data like this in the past using a BinaryWriter, but I've never had to seek through it in order to skip data. What am I doing wrong here?
I have to request data from an external existing webservice written in C#.
This webservice requires some of the data to be encrypted (The connection uses an SSL connection, some of the data is aes encrypted)
On the php site openssl is used for decrypting.
The following settings are used on the c# site
(This are the default values for the AesCryptoServiceProvider):
Algorithm: AES
Padding: PKCS7
Mode: CBC
Keysize: 256
The padding for PKCS7 works as following:
01 If 1 byte is missing
02 02 If 2 bytes are missing
and so on
so this values are not added by the padding.
What am I doing wrong?
I've checked this with c#, php and ruby - the decrypted data starts with 255, 254
To reproduce use the following parameters:
data:1234567890123456
key: First1
salt(iv):Data
using System;
using System.Security.Cryptography;
using System.Text;
using System.IO;
namespace crypto_test
{
class MainClass
{
public static void Main(string[] args)
{
bool running = true;
while (running)
{
Console.WriteLine("Enter data:");
var data = Console.ReadLine();
Console.WriteLine("Enter key:");
var key = Console.ReadLine();
Console.WriteLine("Enter iv:");
var iv = Console.ReadLine();
Console.WriteLine("Enter d for decode");
var decode = (Console.ReadLine() == "d");
string encoded=Crypt(data, key, iv, decode);
Console.WriteLine(encoded);
if (!decode)
{
encoded= Crypt(encoded, key, iv, true);
Console.WriteLine(encoded);
}
Console.WriteLine("quit to exit");
running = !(Console.ReadLine() == "quit");
}
}
public static string Crypt(string value, string password, string salt, bool decrypt)
{
DeriveBytes rgb = new Rfc2898DeriveBytes(password, Encoding.Unicode.GetBytes(salt));
SymmetricAlgorithm algorithm = new AesCryptoServiceProvider();
byte[] rgbKey = rgb.GetBytes(algorithm.KeySize >> 3);
byte[] rgbIV = rgb.GetBytes(algorithm.BlockSize >> 3);
Console.WriteLine("rbKey: size:{0} key:{1}", (algorithm.KeySize >> 3), GetHex(rgbKey));
Console.WriteLine("rgbIV: size:{0} key:{1}", (algorithm.BlockSize >> 3), GetHex(rgbIV));
ICryptoTransform transform = decrypt ? algorithm.CreateDecryptor(rgbKey, rgbIV) : algorithm.CreateEncryptor(rgbKey, rgbIV);
Console.WriteLine("Mode {0}", algorithm.Mode);
Console.WriteLine("PAdding {0}", algorithm.Padding);
using (MemoryStream buffer = new MemoryStream())
{
using (CryptoStream stream = new CryptoStream(buffer, transform, CryptoStreamMode.Write))
{
try
{
if (decrypt)
{
byte[] data = Convert.FromBase64String(value);
stream.Write(data,0,data.Length);
}
else
{
using (StreamWriter writer = new StreamWriter(stream, Encoding.Unicode))
{
writer.Write(value);
}
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
byte[] buff = buffer.ToArray();
if (decrypt)
{
return Encoding.Unicode.GetString(buff) + "\r\n" + GetHex(buff);
}
else
return Convert.ToBase64String(buff);
}
}
public static string GetHex(byte[] data)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < data.Length; ++i)
sb.Append(data[i].ToString("X2"));
return sb.ToString();
}
}
}
I have not found an equivalent to Rfc2898DeriveBytes until now,
so I copied the key and iv
php
<?php
$salt='Data';
$pass='First1';
$data='1234567890123456';
$encrypted_base64='VKNd9Pi+cttaM6ne8pzAuFbH1U0gJiJ2Wlbbr1rU5z8vbIfAS6nb0/5py4p54aK7';
$encrypted=base64_decode($encrypted_base64);
$key = pack('H*', "30EE7F95F0EF4835F048A481424F2F52EE21B7CEB97F8CC437E5949DB53797D9");
$iv = pack('H*', "B29F5ECF7057065758102385509F0637");
$cipher='AES-256-CBC';
$decrypted = openssl_decrypt($encrypted,$cipher, $key,true,$iv);
for($i =0; $i<strlen($decrypted);++$i)
{
echo "char:" . ord($decrypted[$i])."<br/>";
}
echo $decrypted
?>
ruby:
require ('openssl')
require ('base64')
while true
enc_data='VKNd9Pi+cttaM6ne8pzAuFbH1U0gJiJ2Wlbbr1rU5z8vbIfAS6nb0/5py4p54aK7'
data = Base64.decode64(enc_data)
key_hex='30EE7F95F0EF4835F048A481424F2F52EE21B7CEB97F8CC437E5949DB53797D9'
iv_hex='B29F5ECF7057065758102385509F0637'
key = [key_hex].pack('H*')
iv = [iv_hex].pack('H*')
decipher = OpenSSL::Cipher::AES.new(256, :CBC)
decipher.decrypt
decipher.key = key
decipher.iv = iv
plain = decipher.update(data) + decipher.final
puts plain
puts plain.bytes
end
Good news, your decryption seems to work OK.
What you are seeing in the decrypted ciphertext is the byte order mark for UTF-16 LE, which is (incorrectly) indicated by Microsoft as Encoding.Unicode. You need to do either one off two things:
decode the text with a decoder that groks UTF-16 LE including byte order mark;
encode using much more reasonable UTF-8 encoding (in the C# code).
Personally I would put a strong preference on (2).
In our application we are using Triple DES to encrypt and decrypt the data. We have the enc/dec code in C# which uses 24 byte key and 12 byte IV which works fine. Now we want to implement same code in java but when I use 12 byte IV, I get an error in java saying wrong IV size. When I googled around, I came to know that java uses 8 byte IV. Now I am confused as how come there is implementation difference in C# and JAVA for triple DES. Or am I missing anything?
This is something similar to our encryption code
class cTripleDES
{
// define the triple des provider
private TripleDESCryptoServiceProvider m_des = new TripleDESCryptoServiceProvider();
// define the string handler
private UTF8Encoding m_utf8 = new UTF8Encoding();
// define the local property arrays
private byte[] m_key;
private byte[] m_iv;
public cTripleDES(byte[] key, byte[] iv)
{
this.m_key = key;
this.m_iv = iv;
}
public byte[] Encrypt(byte[] input)
{
return Transform(input,
m_des.CreateEncryptor(m_key, m_iv));
}
public byte[] Decrypt(byte[] input)
{
return Transform(input,
m_des.CreateDecryptor(m_key, m_iv));
}
public string Encrypt(string text)
{
byte[] input = m_utf8.GetBytes(text);
byte[] output = Transform(input,
m_des.CreateEncryptor(m_key, m_iv));
return Convert.ToBase64String(output);
}
public string Decrypt(string text)
{
byte[] input = Convert.FromBase64String(text);
byte[] output = Transform(input,
m_des.CreateDecryptor(m_key, m_iv));
return m_utf8.GetString(output);
}
private byte[] Transform(byte[] input,
ICryptoTransform CryptoTransform)
{
// create the necessary streams
MemoryStream memStream = new MemoryStream();
CryptoStream cryptStream = new CryptoStream(memStream,
CryptoTransform, CryptoStreamMode.Write);
// transform the bytes as requested
cryptStream.Write(input, 0, input.Length);
cryptStream.FlushFinalBlock();
// Read the memory stream and
// convert it back into byte array
memStream.Position = 0;
byte[] result = memStream.ToArray();
// close and release the streams
memStream.Close();
cryptStream.Close();
// hand back the encrypted buffer
return result;
}
}
This is how we are utilizing it:
string IVasAString = "AkdrIFjaQrRQ";
byte[] iv = Convert.FromBase64String(IVasAString);
byte[] key = ASCIIEncoding.UTF8.GetBytes(KEY);
// instantiate the class with the arrays
cTripleDES des = new cTripleDES(key, iv);
string output = des.Encrypt("DATA TO BE ENCRYPTED");
TripleDES has a 64-bit block size. You need to use an 8 byte IV in C#.
Got the answer.
decodeBase64 method from apache common framework (commons.codec.binary.Base64) does the necessary.
Thanks mfanto for the heads up.!
I am building a iPhone app which uses c# web services. My c# web services takes user details and validates against my DB and returns xml files.
So Now the issue is how to encrypt user details(username and password are 10chars each) in objective c and decrypt in C#.
I am very new to cryptography, which method is the best. will it be possible to encrypt in Objective c and Decrypt in C#.
thanks..
Thanks for the rapid replies. I appreciate your help. I found a blog which explains my problem. Here is the link for it.
http://dotmac.rationalmind.net/2009/02/aes-interoperability-between-net-and-iphone/
I am implementing it now. I will let you know the status soon.
Thanks a lot..
Happy coding..
On the assumption that you're encrypting this information in order to protect it over the network, the best solution is to connect over SSL. This will address the problem without creating new complexities in the code. SSL handling is generally available in both .NET and Cocoa.
Is there some other reason that you're trying to encrypt this data?
The following is from the The CSharp Cookbook. It is about as straight forwand an example as exists. You would of course have to port the encrypt portion to Objective C, but so long as you used the same Key, it should generate the same result.
You need to use Rijndael cipher which is available here in ObjC compatible code
public static void EncDecString()
{
string encryptedString = CryptoString.Encrypt("MyPassword");
Console.WriteLine("encryptedString: " + encryptedString);
// get the key and IV used so you can decrypt it later
byte [] key = CryptoString.Key;
byte [] IV = CryptoString.IV;
CryptoString.Key = key;
CryptoString.IV = IV;
string decryptedString = CryptoString.Decrypt(encryptedString);
Console.WriteLine("decryptedString: " + decryptedString);
}
public sealed class CryptoString
{
private CryptoString() {}
private static byte[] savedKey = null;
private static byte[] savedIV = null;
public static byte[] Key
{
get { return savedKey; }
set { savedKey = value; }
}
public static byte[] IV
{
get { return savedIV; }
set { savedIV = value; }
}
private static void RdGenerateSecretKey(RijndaelManaged rdProvider)
{
if (savedKey == null)
{
rdProvider.KeySize = 256;
rdProvider.GenerateKey();
savedKey = rdProvider.Key;
}
}
private static void RdGenerateSecretInitVector(RijndaelManaged rdProvider)
{
if (savedIV == null)
{
rdProvider.GenerateIV();
savedIV = rdProvider.IV;
}
}
public static string Encrypt(string originalStr)
{
// Encode data string to be stored in memory
byte[] originalStrAsBytes = Encoding.ASCII.GetBytes(originalStr);
byte[] originalBytes = {};
// Create MemoryStream to contain output
MemoryStream memStream = new MemoryStream(originalStrAsBytes.Length);
RijndaelManaged rijndael = new RijndaelManaged();
// Generate and save secret key and init vector
RdGenerateSecretKey(rijndael);
RdGenerateSecretInitVector(rijndael);
if (savedKey == null || savedIV == null)
{
throw (new NullReferenceException(
"savedKey and savedIV must be non-null."));
}
// Create encryptor, and stream objects
ICryptoTransform rdTransform =
rijndael.CreateEncryptor((byte[])savedKey.Clone(),
(byte[])savedIV.Clone());
CryptoStream cryptoStream = new CryptoStream(memStream, rdTransform,
CryptoStreamMode.Write);
// Write encrypted data to the MemoryStream
cryptoStream.Write(originalStrAsBytes, 0, originalStrAsBytes.Length);
cryptoStream.FlushFinalBlock();
originalBytes = memStream.ToArray();
// Release all resources
memStream.Close();
cryptoStream.Close();
rdTransform.Dispose();
rijndael.Clear();
// Convert encrypted string
string encryptedStr = Convert.ToBase64String(originalBytes);
return (encryptedStr);
}
public static string Decrypt(string encryptedStr)
{
// Unconvert encrypted string
byte[] encryptedStrAsBytes = Convert.FromBase64String(encryptedStr);
byte[] initialText = new Byte[encryptedStrAsBytes.Length];
RijndaelManaged rijndael = new RijndaelManaged();
MemoryStream memStream = new MemoryStream(encryptedStrAsBytes);
if (savedKey == null || savedIV == null)
{
throw (new NullReferenceException(
"savedKey and savedIV must be non-null."));
}
// Create decryptor, and stream objects
ICryptoTransform rdTransform =
rijndael.CreateDecryptor((byte[])savedKey.Clone(),
(byte[])savedIV.Clone());
CryptoStream cryptoStream = new CryptoStream(memStream, rdTransform,
CryptoStreamMode.Read);
// Read in decrypted string as a byte[]
cryptoStream.Read(initialText, 0, initialText.Length);
// Release all resources
memStream.Close();
cryptoStream.Close();
rdTransform.Dispose();
rijndael.Clear();
// Convert byte[] to string
string decryptedStr = Encoding.ASCII.GetString(initialText);
return (decryptedStr);
}
}
I can't speak regarding the Objective C implementation of Rijndael cipher, but I have used this(C#) code for the basis of some production work and it has worked wonderfully.
Your question is very vague, but in short; yes, it is possible. You will need to figure out what the expectations of your cryptography are (high security or high speed?) and then weigh the benefits of various algorthms and their implementation difficulties in Objective-C and C#.
I don't know Objective C, but for the C# decryption, look into the various CryptoServiceProviders in System.Security.Cryptography.
Here is one example I wrote using TripleDES:
public class TripleDES
{
private byte[] mbKey;
private byte[] mbIV;
private TripleDESCryptoServiceProvider tdProvider = new TripleDESCryptoServiceProvider();
private UTF8Encoding UTEncode = new UTF8Encoding();
// Key: **YOUR KEY**
// Project IV: **YOUR IV**
public TripleDES(string strKey, string strIV)
{
mbKey = UTEncode.GetBytes(strKey);
mbIV = UTEncode.GetBytes(strIV);
}
public TripleDES()
{
//
// TODO: Add constructor logic here
//
}
public string EncryptToString(string strInput)
{
return Convert.ToBase64String(this.EncryptToBytes(strInput));
}
public byte[] EncryptToBytes(string strInput)
{
byte[] bInput = UTEncode.GetBytes(strInput);
byte[] bOutput = ProcessInput(bInput, tdProvider.CreateEncryptor(mbKey, mbIV));
return bOutput;
}
public string DecryptToString(string strInput)
{
return UTEncode.GetString(DecryptToBytes(strInput));
}
public byte[] DecryptToBytes(string strInput)
{
byte[] bInput = Convert.FromBase64String(strInput);
byte[] bOutput = ProcessInput(bInput, tdProvider.CreateDecryptor(mbKey, mbIV));
return bOutput;
}
private byte[] ProcessInput(byte[] input, ICryptoTransform ctProcessor)
{
MemoryStream memStream = new MemoryStream();
CryptoStream crpStream = new CryptoStream(memStream, ctProcessor, CryptoStreamMode.Write);
crpStream.Write(input, 0, input.Length);
crpStream.FlushFinalBlock();
memStream.Position = 0;
byte[] output;
output = new byte[memStream.Length];
memStream.Read(output, 0, output.Length);
memStream.Close();
crpStream.Close();
return output;
}
}
}