I have my c# server and my java client, their communication is encrypted but when the client sends an encrypted query, the server cant decrypt it, well it can but its totally unreadable, its like converting a byte array to a string, totally unreadable, this is the encryption on the clients side:
public byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException
{
byte[] encryptedBytes = null;
byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
final Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5PADDING);
final byte[] keyData = Arrays.copyOf(passwordBytes, KEY_SIZE
/ Byte.SIZE);
final byte[] ivBytes = Arrays.copyOf(keyData, cipher.getBlockSize());
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyData, "AES"),
new IvParameterSpec(ivBytes));
encryptedBytes = cipher.doFinal(bytesToBeEncrypted);
return encryptedBytes;
}
And the decryption on the server side:
internal string DecryptText(string inputString, Key k)
{
try
{
inputString = inputString.Replace("\0", "");
byte[] decryptedBytes = null;
byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
byte[] bytesToBeDecrypted = Convert.FromBase64String(inputString);
byte[] passwordBytes = Encoding.UTF8.GetBytes("azaz");
passwordBytes = SHA256.Create().ComputeHash(passwordBytes);
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Padding = PaddingMode.None;
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
cs.Close();
}
decryptedBytes = ms.ToArray();
}
}
return Encoding.UTF8.GetString(decryptedBytes);
}
catch (Exception ex)
{
throw new SystemException(ex.Message);
}
}
Thanks in advance
EDIT On the java client, this function call the AES_Encrypt function:
public String EncryptText(String input) throws NoSuchAlgorithmException
{
byte[] bytesToBeEncrypted = input.getBytes();
byte[] passwordBytes = Config.ServerKey.getBytes();
MessageDigest md = MessageDigest.getInstance("SHA-256");
passwordBytes = md.digest(passwordBytes);
byte[] bytesEncrypted = null;
try {
bytesEncrypted = AES_Encrypt(bytesToBeEncrypted, passwordBytes);
} catch (NoSuchPaddingException ex) {
Logger.getLogger(CryptoClass.class.getName()).log(Level.SEVERE, null, ex);
} catch (InvalidAlgorithmParameterException ex) {
Logger.getLogger(CryptoClass.class.getName()).log(Level.SEVERE, null, ex);
} catch (InvalidKeyException ex) {
Logger.getLogger(CryptoClass.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalBlockSizeException ex) {
Logger.getLogger(CryptoClass.class.getName()).log(Level.SEVERE, null, ex);
} catch (BadPaddingException ex) {
Logger.getLogger(CryptoClass.class.getName()).log(Level.SEVERE, null, ex);
}
return Base64.getEncoder().encodeToString(bytesEncrypted);
}
EDIT Implemented basic SslStream and verification server side
private void Do()
{
int requestCount = 0;
string serverResponse = null;
string rCount = null;
string dataFromClient = null;
Byte[] sendBytes = null;
requestCount = 0;
Responder.Responder R = new Responder.Responder();
while ((true))
{
try
{
byte[] buffer = new byte[4];
requestCount = requestCount + 1;
bool leaveInnerStreamOpen = true;
RemoteCertificateValidationCallback validationCallback =
new RemoteCertificateValidationCallback(ClientValidationCallback);
LocalCertificateSelectionCallback selectionCallback =
new LocalCertificateSelectionCallback(ServerCertificateSelectionCallback);
EncryptionPolicy encryptionPolicy = EncryptionPolicy.AllowNoEncryption;
_sslStream = new SslStream(clientSocket.GetStream(),
leaveInnerStreamOpen, validationCallback, selectionCallback, encryptionPolicy);
X509Certificate2 certificate = ServerCertificate.Servercertificate(); //method that has access to the embedded certificate
bool requireClientCertificate = true;
SslProtocols enabledSslProtocols = SslProtocols.Tls11 | SslProtocols.Tls12;
bool checkCertificateRevocation = true;
_sslStream.AuthenticateAsServer
(certificate, requireClientCertificate, enabledSslProtocols, checkCertificateRevocation);
buffer = new byte[4];
int readBytes = _sslStream.Read(buffer, 0, 4);
if (readBytes == 0)
break;
int MessageSize = BitConverter.ToInt32(buffer, 0);
byte[] bufferreader = new byte[MessageSize];
clientSocket.ReceiveBufferSize = MessageSize;
readBytes = _sslStream.Read(bufferreader, 0, MessageSize);
Console.WriteLine(Convert.ToString(MessageSize));
rCount = Convert.ToString(requestCount);
dataFromClient = Encoding.ASCII.GetString(bufferreader);
byte[] outbuffer = new byte[4];
serverResponse = R.Respond(dataFromClient, K, clientSocket);
sendBytes = Encoding.ASCII.GetBytes(serverResponse);
outbuffer = new byte[4];
outbuffer = BitConverter.GetBytes(sendBytes.Length);
_sslStream.Write(outbuffer, 0, 4);
_sslStream.Flush();
clientSocket.SendBufferSize = sendBytes.Length;
MessageBox.Show(serverResponse);
_sslStream.Write(sendBytes, 0, sendBytes.Length);
_sslStream.Flush();
}
catch (Exception ex)
{
EndPointHandler.RemoveEndPoint(clientSocket);
clientSocket.Close();
Console.WriteLine("User Server >> " + ex.ToString());
Thread.CurrentThread.Abort();
}
}
EndPointHandler.RemoveEndPoint(clientSocket);
Console.WriteLine("User Server >> " + "Client No:" + Convert.ToString(clNo) + " Stopped!");
}
private bool ClientValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
switch (sslPolicyErrors)
{
case SslPolicyErrors.RemoteCertificateNameMismatch:
Console.WriteLine("Client's name mismatch. End communication ...\n");
return false;
case SslPolicyErrors.RemoteCertificateNotAvailable:
Console.WriteLine("Client's certificate not available. End communication ...\n");
return false;
case SslPolicyErrors.RemoteCertificateChainErrors:
Console.WriteLine("Client's certificate validation failed. End communication ...\n");
return false;
}
Console.WriteLine("Client's authentication succeeded ...\n");
return true;
}
private X509Certificate ServerCertificateSelectionCallback(object sender, string targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers)
{
return ServerCertificate.Servercertificate();
}
In your Java code you have:
final Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5PADDING);
Which likely means you are using PKCS5/7 Padding, unless you are just really terrible at picking names for your constants.
However, in your C# code, you have:
AES.Padding = PaddingMode.None;
Which is clearly not PKCS5/7 Padding... So you should probably change that to PaddingMode.PKCS7.
EDIT: Please also don't disregard my comment about your IV. If you want your code to actually be worth using, you should automatically generate the IV for each encryption and prepend it to the ciphertext.
Also just noticed that you derive your key data in different ways. You are using PBKDF2 w/ SHA1 in C# (Rfc2898DeriveBytes) but using a single iteration of SHA256 in Java. You'll need to pick one or another.
Related
I have a client/server application that encrypts or decrypts a message both on the client-side and server-side. However on decryption I keep getting the following error from the StackTrace
Input length must be multiple of 8 when decrypting with padded cipher
Following is the code from both the Server and Client classes.
The thing is I have tried using ("DES/CBC/PKCS5Padding") and ("UTF-8") but still haven't been able to fix said issue.
Any help would be appreciated thanks?
public class ServerApp {
public static byte[] encrypt(String input, Key k) {
try {
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, k);
byte[] data = input.getBytes();
byte[] result = cipher.doFinal(data);
return result;
} catch (Exception ex) {
return null;
}
}
public static String decrypt(byte[] cipher, Key k) {
try {
Cipher cipher1 = Cipher.getInstance("DES");
cipher1.init(Cipher.DECRYPT_MODE, k);
byte[] original = cipher1.doFinal(cipher);
return new String(original);
} catch (Exception ex) {
return null;
//Logger.getLogger(DES.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static void main(String[] args) throws NoSuchAlgorithmException {
try {
Key key=KeyGen.getSecretKey();
ServerSocket ser = new ServerSocket(3333);
System.out.println("Server Started");
Socket client = ser.accept();
DataInputStream in = new DataInputStream(client.getInputStream());
DataOutputStream out = new DataOutputStream(client.getOutputStream());
Scanner scan = new Scanner(System.in);
// SecretSocket sc = new SecretSocket(client, KeyGen.getSecretKey());
String serMsg, cliMsg, plain;
// OutputStream sout = sc.getOutputStream();
// InputStream sin = sc.getInputStream();
do {
System.out.print("You say: ");
serMsg = scan.nextLine();
// sout.write(serMsg.getBytes());
// System.out.println("cli server "+sin.read());
byte[] ci = encrypt(serMsg, KeyGen.getSecretKey());
System.out.println("encrypt " +ci.toString());
out.writeUTF(ci.toString());
plain = decrypt(ci, KeyGen.getSecretKey() );
System.out.println("decrypt " + plain);
} while (!serMsg.equals("end"));
client.close();
ser.close();
} catch (IOException ex) {
Logger.getLogger(ServerApp.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
public class ClientApp {
public static byte[] encrypt(String input, Key k) {
try {
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, k);
byte[] data = input.getBytes();
byte[] result = cipher.doFinal(data);
return result;
} catch (Exception ex) {
return null;
}
}
public static String decrypt(byte[] cipher, Key k) {
try {
Cipher cipher1 = Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher1.init(Cipher.DECRYPT_MODE, k);
byte[] original = cipher1.doFinal(cipher);
return new String(original);
} catch (Exception ex) {
return null;
//Logger.getLogger(DES.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
try {
Socket s = new Socket("localhost", 3333);
DataInputStream in = new DataInputStream(s.getInputStream());
DataOutputStream out = new DataOutputStream(s.getOutputStream());
Scanner scan = new Scanner(System.in);
String serMsg, cliMsg, plain;
do {
System.out.println("server server "+in.readUTF());
serMsg = in.readUTF();
System.out.println("enc: " + serMsg);
plain = decrypt(serMsg.getBytes("UTF-8"), KeyGen.getSecretKey());
System.out.println("Server says: " + plain);
} while (!serMsg.equals("end"));
s.close();
} catch (IOException ex) {
Logger.getLogger(ClientApp.class.getName()).log(Level.SEVERE, null, ex);
}
}
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, k);
byte[] data = input.getBytes();
byte[] result = cipher.doFinal(data);
Using DES without IV you are implicitly using DES/ECB/PKCS5Padding.
do not print a byte array directly. Java will output only printable characters. Always encode and decode a byte array when printing (Hex or Base64 are the most common encodings)
Here is an example project
Base64.getEncoder().encodeToString(byteArray)
I hope you are aware DES is considered a weak cipher today and should be used only for backward compatibility.
My application requires the users to upload digitally signed pdf and then encrypt the file. This encrypted file is then uploaded on server, where it is decrypted. The decrypted file is then verified by matching the hash of file and digital signature. Now this file is encrypted with using AES algorithm. Once encryption is completed the file is then stored on file server. The size of file could go upto 80mb.
The challenge I am facing now is that when the encrypted file is stored on local drive of machine the files get saved instantly but when the file server is on another machine it takes upto 30 min to save a single file. I am not able to figure out the reason for it.
Following is the code which I am using. I have deployed and tried in tomcat 6 and IBM WAS. The file transfer takes the same time when transferring to file server. The file server is connected to Application server via SAN network.
Following is my encryption code
strSymAlg = rb.getString("SYM_KEY_ALG"); //SYM_KEY_ALG=AES
cipher = Cipher.getInstance(strSymAlg);
SecKey = new SecretKeySpec(hex2Byte(sSymmetricKey), strSymAlg);
cipher.init(Cipher.DECRYPT_MODE, SecKey);
baos = recoverFile(new FileInputStream(fileEnv), cipher);
if (baos != null && isRecoveredFileValid((InputStream) new ByteArrayInputStream(baos.toByteArray()))) {
fileRecovered = (InputStream) new ByteArrayInputStream(baos.toByteArray());
}
}
private ByteArrayOutputStream recoverFile(FileInputStream in, Cipher cipher) {
int blockSize = cipher.getBlockSize();
int outputSize = cipher.getOutputSize(blockSize);
byte[] inBytes = new byte[blockSize];
byte[] outBytes = new byte[outputSize];
int inLength = 0;
int outLength = 0;
boolean more = true;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
while (more) {
inLength = in.read(inBytes);
if (inLength == blockSize) {
outLength = cipher.update(inBytes, 0, blockSize, outBytes);
baos.write(outBytes, 0, outLength);
} else {
more = false;
}
}
if (inLength > 0) {
outBytes = cipher.doFinal(inBytes, 0, inLength);
} else {
outBytes = cipher.doFinal();
}
baos.write(outBytes);
} catch (Exception e) {
System.out.println("recoverFile1: " + e.getMessage());
// e.printStackTrace();
baos = null;
}
return baos;
}
my encryption code is
String strSymKey = "";
File fileToCreate = null;
KeyGenerator keygen = KeyGenerator.getInstance(strSymAlg);
random = new SecureRandom();
keygen.init(random);
SecretKey secKey = keygen.generateKey();
Key publicKey = getPublicKeyFromString(sPubKey.trim());
//encrypt Symmetric key with public key
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.WRAP_MODE, publicKey);
byte[] wrappedKey = cipher.wrap(secKey);
strSymKey = byte2hex(wrappedKey);
fileToCreate = new File(strFile);
if (fileToCreate.exists()) {
fileToCreate.delete();
}
//Encrypt Bidder file with symmetric key
DataOutputStream out = new DataOutputStream(new FileOutputStream(strFile));
cipher = Cipher.getInstance(strSymAlg);
cipher.init(Cipher.ENCRYPT_MODE, secKey);
crypt(fis, out, cipher);
fis.close();
out.close();
//blnDone=true;
// System.out.println("STRING SYMMETRIC KEY:"+ strSymKey);
return strSymKey;
public String byte2hex(byte[] b) {
// String Buffer can be used instead
String hs = "";
String stmp = "";
for (int n = 0; n < b.length; n++) {
stmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
if (stmp.length() == 1) {
hs = hs + "0" + stmp;
} else {
hs = hs + stmp;
}
if (n < b.length - 1) {
hs = hs + "";
}
}
return hs;
}
New Function added
public void crypt(InputStream in, OutputStream out, Cipher cipher) throws IOException, GeneralSecurityException {
System.out.println("crypt start time :"+ new Date());
int blockSize = cipher.getBlockSize();
int outputSize = cipher.getOutputSize(blockSize);
byte[] inBytes = new byte[blockSize];
byte[] outBytes = new byte[outputSize];
int inLength = 0;
boolean more = true;
while (more) {
inLength = in.read(inBytes);
if (inLength == blockSize) {
int outLength = cipher.update(inBytes, 0, blockSize, outBytes);
out.write(outBytes, 0, outLength);
} else {
more = false;
}
}
if (inLength > 0) {
outBytes = cipher.doFinal(inBytes, 0, inLength);
} else {
outBytes = cipher.doFinal();
}
System.out.println("crypt end time :"+ new Date());
out.write(outBytes);
}
Thanks in advance
You are making the classic mistake of assuming that every read fills the buffer, and another: that it won't do so at end of stream. Neither is correct.
while ((count = in.read(buffer)) >= 0)
{
out.write(cipher.update(buffer, 0, count));
}
out.write(cipher.doFinal());
You don't need a DataOutputStream for this.
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 wrote 2 functions in Java to encrypt and decrypt the message. I chose AES/CBC/PKCS7Padding as parameter. while encryption, my function can't add the right padding(as PKCS7Padding, it shouldn't add 0 to pad my message), so while decryption i can't remove the 0 at the end of the message.
with code, my problem will be more clair
public static byte[] encryptStringToData(String message, String key){
// transform the message from string to bytes
byte[] array_to_encrypt, bkey;
try {
array_to_encrypt = message.getBytes("UTF8");
bkey = key.getBytes("UTF8");
} catch (UnsupportedEncodingException e1) {
array_to_encrypt = null;
bkey = null;
return null;
}
bkey = Arrays.copyOf(bkey, 32);
BlockCipher engine = new AESEngine();
engine.init(true, new KeyParameter(bkey, 0, 32));
PKCS7Padding pad = new PKCS7Padding() ;
BufferedBlockCipher c = new PaddedBufferedBlockCipher(new CBCBlockCipher(engine),pad);
c.init(true, new ParametersWithIV(new KeyParameter(bkey), new byte[16]));
byte[] encrypted_array = new byte[c.getOutputSize(array_to_encrypt.length)];
int outputLen = c.processBytes(array_to_encrypt, 0, array_to_encrypt.length, encrypted_array, 0);
try
{
c.doFinal(encrypted_array, outputLen);
}
catch (CryptoException ce)
{
System.err.println(ce);
System.exit(1);
}
return encrypted_array;
}
public static String decryptDataToString(byte[] message, String key){
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
String decrypted ="";
try {
byte[] keyBytes = key.getBytes("UTF8");
BlockCipher engine = new AESEngine();
BufferedBlockCipher c = new PaddedBufferedBlockCipher(new CBCBlockCipher(engine));
keyBytes = Arrays.copyOf(keyBytes, 32); // use only first 256 bit
c.init(false, new ParametersWithIV(new KeyParameter(keyBytes), new byte[16]));
byte[] cipherText = new byte[c.getOutputSize(message.length)];
int outputLen = c.processBytes(message, 0, message.length, cipherText, 0);
c.doFinal(cipherText, outputLen);
decrypted = new String(cipherText,"UTF8");
}
catch (CryptoException ce)
{
System.err.println(ce);
System.exit(1);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return decrypted;
}
result (hex output)
clair message :3132333435 size: 5
encrypted message:561dd9f43ec183fe351776a46276991c size: 16
decrypted message:31323334350000000000000000000000 size: 16
You should check How do I get started using BouncyCastle?
The code for removing extra bytes is:
if (outputLength == output.length) {
return output;
} else {
byte[] truncatedOutput = new byte[outputLength];
System.arraycopy(
output, 0,
truncatedOutput, 0,
outputLength
);
return truncatedOutput;
}
and in your code traslates to:
outputLen += c.doFinal(cipherText, outputLen);
if (outputLen == cipherText.length) {
decrypted = new String(cipherText,"UTF8");
} else {
byte[] truncatedOutput = new byte[outputLen];
System.arraycopy(
cipherText, 0,
truncatedOutput, 0,
outputLen
);
decrypted = new String(truncatedOutput,"UTF8");
}