Related
I am trying to learn AES by testing my code against https://aesencryption.net. I previously had an error in Base64.encodeBase64String and also Base64.decodeBase64 // encode/decode Base64. So I manipulated Base64 somehow to resolve the error. Now in my app the text is encrypted and decrypted properly, I think. But when I try to encrypt or decrypt the same text server-side (at aesencryption.net), the site is not able to decrypt my encrypted string. Please help.
Following is my code :
public class MainActivity extends AppCompatActivity {
static final String TAG = "SymmetricAlgorithmAES";
private static SecretKeySpec secretKey ;
private static byte[] key ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Original text
// {"type":"Success","httpCode":"200","code":"200","message":{"pin":"11111"},"extra":""}
String theTestText = "hi";
TextView tvorig = (TextView)findViewById(R.id.tvorig);
tvorig.setText("\n[ORIGINAL]:\n" + theTestText + "\n");
final String strPssword = "android";
setKey(strPssword);
// Encode the original data with AES
byte[] encodedBytes = null;
try {
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE,secretKey);
encodedBytes = c.doFinal(theTestText.getBytes());
} catch (Exception e) {
Log.e(TAG, "AES encryption error");
}
TextView tvencoded = (TextView)findViewById(R.id.tvencoded);
tvencoded.setText("[ENCODED]:\n" +
Base64.encodeToString(encodedBytes, Base64.DEFAULT) + "\n");
Log.d(TAG, Base64.encodeToString(encodedBytes, Base64.DEFAULT));
// Decode the encoded data with AES
byte[] decodedBytes = null;
try {
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.DECRYPT_MODE, secretKey);
decodedBytes = c.doFinal(encodedBytes);
} catch (Exception e) {
Log.e(TAG, "AES decryption error");
}
TextView tvdecoded = (TextView)findViewById(R.id.tvdecoded);
tvdecoded.setText("[DECODED]:\n" + new String(decodedBytes) + "\n");
}
public static void setKey(String myKey){
MessageDigest sha = null;
try {
key = myKey.getBytes("UTF-8");
System.out.println(key.length);
sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16); // use only first 128 bit
System.out.println(key.length);
System.out.println(new String(key,"UTF-8"));
secretKey = new SecretKeySpec(key, "AES");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
Thanks in advance.
I do something like this which actually works ;)
public class AESCrypter {
private final Cipher cipher;
private final SecretKeySpec key;
private AlgorithmParameterSpec spec;
public AESCrypter(String password) throws Exception
{
// hash password with SHA-256 and crop the output to 128-bit for key
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(password.getBytes("UTF-8"));
byte[] keyBytes = new byte[32];
System.arraycopy(digest.digest(), 0, keyBytes, 0, keyBytes.length);
cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
key = new SecretKeySpec(keyBytes, "AES");
spec = getIV();
}
public AlgorithmParameterSpec getIV()
{
byte[] iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
IvParameterSpec ivParameterSpec;
ivParameterSpec = new IvParameterSpec(iv);
return ivParameterSpec;
}
public String encrypt(String plainText) throws Exception
{
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
byte[] encrypted = cipher.doFinal(plainText.getBytes("UTF-8"));
String encryptedText = new String(Base64.encode(encrypted, Base64.DEFAULT), "UTF-8");
return encryptedText;
}
public String decrypt(String cryptedText) throws Exception
{
cipher.init(Cipher.DECRYPT_MODE, key, spec);
byte[] bytes = Base64.decode(cryptedText, Base64.DEFAULT);
byte[] decrypted = cipher.doFinal(bytes);
String decryptedText = new String(decrypted, "UTF-8");
return decryptedText;
}
}
Call this class like this :
try {
AESCrypter _crypt = new AESCrypter("password");
String output = "";
String plainText = "top secret message";
output = _crypt.encrypt(plainText); //encrypt
System.out.println("encrypted text=" + output);
output = _crypt.decrypt(output); //decrypt
System.out.println("decrypted text=" + output);
} catch (Exception e) {
e.printStackTrace();
}
And for iPhone (Code is here) : https://github.com/Gurpartap/AESCrypt-ObjC
Hope this code works for you too :)
The PHP code uses CBC mode by default while your Java code does not specify the same, and leaves it to the underlying implementation. If I recall correctly its probably ECB/PKCS5Padding. Your PHP implementation is using 'CBC' with no padding (mcrypt doesn't support padding by default but you can do so manually).
Its very important be be very specific about mode, padding, and charset when working with different platforms else you will run into different defaults.
Try initializing your cipher as follows: Cipher.getInstance("AES/CBC/NoPadding");
fun encrypt(encrypted: String): String? {
try {
val secretKey = "secretKey" //The secret key, 32 bytes string.
val ivKey = "ivKey" // The initialization vector, 16 bytes string.
val iv = IvParameterSpec(hashIV.toByteArray(Charsets.UTF_8))
val skySpec = SecretKeySpec(hashKey.toByteArray(Charsets.UTF_8), "AES-256-CBC")
val cipher: Cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING")
cipher.init(Cipher.ENCRYPT_MODE, skySpec, iv)
val original: ByteArray = cipher.doFinal(encrypted.toByteArray())
return Base64.encode(
Base64.encodeToString(original, Base64.NO_WRAP).toByteArray(charset("UTF-8")),
Base64.NO_WRAP
).decodeToString()
} catch (ex: Exception) {
ex.printStackTrace()
}
return null
}
fun decrypt(encrypted: String?): String? {
try {
val secretKey = "secretKey" //The secret key, 32 bytes string.
val ivKey = "ivKey" // The initialization vector, 16 bytes string.
val iv = IvParameterSpec(hashIV.toByteArray(Charsets.UTF_8))
val skySpec = SecretKeySpec(hashKey.toByteArray(Charsets.UTF_8), algorithm)
val cipher: Cipher = Cipher.getInstance(transformation)
cipher.init(Cipher.DECRYPT_MODE, skySpec, iv)
val original: ByteArray = cipher.doFinal(
Base64.decode(
Base64.decode(encrypted, Base64.NO_WRAP),
Base64.NO_WRAP
)
)
return original.decodeToString()
} catch (ex: Exception) {
ex.printStackTrace()
}
return null
}
openssl_encrypt
I am trying to write a program to encrypt and decrypt files using java, but I get an error on the decrypt function:
Caused by: javax.crypto.BadPaddingException: Given final block not properly padded
here is the code for the encryption:
public static void EncryptFile(String inFile, PublicKey rsaPublicKey) {
AesManaged aesManaged = new AesManaged();
try {
aesManaged.keySize = 256;
aesManaged.blockSize = 128;
aesManaged.mode = "AES/CBC/PKCS5Padding";
byte[] key = generateKey(aesManaged.keySize);
byte[] keyEncrypted = encryptKey(key, rsaPublicKey);
byte[] LenK = new byte[4];
byte[] LenIV = new byte[4];
int lKey = keyEncrypted.length;
LenK = BitConverter.GetBytes(lKey);
int lIV = aesManaged.IV().length;
LenIV = BitConverter.GetBytes(lIV);
// Write the following to the FileStream
// for the encrypted file (outFs):
// - length of the key
// - length of the IV
// - ecrypted key
// - the IV
// - the encrypted cipher content
String outFile = "test.lamar";
ByteArrayOutputStream outFs = new ByteArrayOutputStream();
outFs.write(LenK);
outFs.write(LenIV);
outFs.write(keyEncrypted);
byte[] i = aesManaged.IV();
outFs.write(i);
IvParameterSpec ivspec = new IvParameterSpec(aesManaged.IV());
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance(aesManaged.mode);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivspec);
FileInputStream fileIn = new FileInputStream(inFile);
CipherOutputStream cipherOut = new CipherOutputStream(outFs, cipher);
int blockSiseByte = aesManaged.blockSize / 8;
byte[] data = new byte[blockSiseByte];
int count;
// Read in the data from the file and encrypt it
while ((count = fileIn.read(data, 0, blockSiseByte)) != -1) {
cipherOut.write(data, 0, count);
}
try (OutputStream outputStream = new FileOutputStream(outFile)) {
outFs.writeTo(outputStream);
}
// Close the encrypted file
fileIn.close();
outFs.close();
cipherOut.close();
} catch (Exception e) {
e.printStackTrace();
}
}
and the code for the decryption:
public static void DecryptFile(String inFile, String outFile,
PrivateKey rsaPrivateKey) {
FileOutputStream outFs = null;
try {
// Create instance of AesManaged for
// symetric decryption of the data.
AesManaged aesManaged = new AesManaged();
{
aesManaged.keySize = 256;
aesManaged.blockSize = 128;
aesManaged.mode = "AES/CBC/PKCS5Padding";
// Create byte arrays to get the length of
// the encrypted key and IV.
// These values were stored as 4 bytes each
// at the beginning of the encrypted package.
byte[] LenK = new byte[4];
byte[] LenIV = new byte[4];
// Use FileStream objects to read the encrypted
// file (inFs) and save the decrypted file (outFs).
{
byte[] fileBytes = FileUtils.readFileToByteArray(new File(inFile));
ByteArrayInputStream inFs = new ByteArrayInputStream(
fileBytes);
;
for (int i = 0; i < LenK.length; i++) {
LenK[i] = (byte) inFs.read();
}
for(int i = 0; i< LenIV.length;i++){
LenIV[i] = (byte)inFs.read();
}
// Convert the lengths to integer values.
int lenK = BitConverter.ToInt32(LenK, 0);
int lenIV = BitConverter.ToInt32(LenIV, 0);
//int startC = lenK + lenIV + 8;
//int lenC = (int) fileBytes.length - startC;
// Create the byte arrays for
// the encrypted AesManaged key,
// the IV, and the cipher text.
byte[] KeyEncrypted = new byte[lenK];
byte[] IV = new byte[lenIV];
// Extract the key and IV
for(int i = 0;i<lenK;i++){
KeyEncrypted[i] = (byte)inFs.read();
}
for(int i =0;i<lenIV;i++){
IV[i] = (byte)inFs.read();
}
// to decrypt the AesManaged key.
byte[] KeyDecrypted = decryptKey(KeyEncrypted,rsaPrivateKey);
Cipher transform = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivspec = new IvParameterSpec(IV);
SecretKeySpec secretKeySpec = new SecretKeySpec(KeyDecrypted, "AES");
transform.init(Cipher.DECRYPT_MODE, secretKeySpec, ivspec);
// Decrypt the key.
outFs = new FileOutputStream(outFile);
int count = 0;
int offset = 0;
int blockSizeBytes = aesManaged.blockSize / 8;
byte[] data = new byte[blockSizeBytes];
CipherInputStream cipherIn = new CipherInputStream(
inFs, transform);
while ((count = cipherIn.read(data, 0, blockSizeBytes)) != -1) {
outFs.write(data, 0, count);
}
inFs.close();
cipherIn.close();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
The error occurred at the line:while ((count = cipherIn.read(data, 0, blockSizeBytes)) != -1) after many iterations.
What am I missing here?
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.
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?