So here is a question.
I have an old code on a Windows system that just takes a short string and makes CryptEncrypt on it with a public RSA key.
The minimum working example is here (avoiding any checks to make it shorter. avoiding freeing as well)
std::string testData = "12345678";
HCRYPTPROV context;
CryptAcquireContext(&context, nullptr, nullptr, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT);
auto pubK = FromBase64(PublicKey);
CERT_PUBLIC_KEY_INFO *pubKeyInfo = nullptr;
DWORD keyLength = 0;
CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO,
pubK.data(), pubK.size(),
CRYPT_ENCODE_ALLOC_FLAG, nullptr,
&pubKeyInfo, &keyLength);
HCRYPTKEY key;
CryptImportPublicKeyInfo(context, X509_ASN_ENCODING, pubKeyInfo, &key);
std::vector<std::uint8_t> result(testData.begin(), testData.end());
DWORD size = testData.size();
CryptEncrypt(key, NULL, true, 0, result.data(), &size, result.size());
result.resize(size);
size = testData.size();
CryptEncrypt(key, NULL, true, 0, result.data(), &size, result.size());
std::cout << ToBase64(result) << "\n";
The code works and returns a base64 encoded string. like lTq01sOcgDcgwtDaFFoHH/Qb6xLw0KU+/n/+3t0eEb8l4N69QGcaEWf1qso3a4qn7Y8StlXcfMe8uspNF/KDj6qQOMvCuM+uUl+tkLd5NXiESsjycgjyxAqdCIO71iTSmsYVcsS3fY/gtIbO4UAFnCRPOXoSyqWqpXW7IRtFzL2N3MxgIBlIMErNvNWs5HPA7xAY/XO6UpSMWsQO4ppccdeNLSZDPwOxohKD/BX5oDin81nFn7fvIZgghfH5knF1nezK8IGKl+vtbgrwlUUULp/wJ4POceyIn0HaZoVsaCu6xFJcUJGfBqSvm4GZqkp2MlGxBODku0OSgEfIDEGMTg==.
Then I have to decrypt this string with the private key on another side running java.
I use bouncycastle:
try {
Security.addProvider(new BouncyCastleProvider());
String value = "";
AsymmetricKeyParameter privateKey =
(AsymmetricKeyParameter) PrivateKeyFactory.createKey(Base64.getDecoder().decode(privateKeyValue));
AsymmetricBlockCipher e = new RSAEngine();
e = new org.bouncycastle.crypto.encodings.PKCS1Encoding(e);
e.init(false, privateKey);
byte[] messageBytes = Base64.getDecoder().decode(inputdata);
byte[] hexEncodedCipher = e.processBlock(messageBytes, 0, messageBytes.length);
value = new String(hexEncodedCipher);
System.out.println(value);
return value;
}
catch (Exception e) {
System.out.println(e);
}
And this code shows me the next error:
org.bouncycastle.crypto.DataLengthException: input too large for RSA cipher.
And I believe I'm missing something on the windows side, because if I use the same keys on the Java side and crypt the same data with the same public key, the decryption works as expected.
Here the keys, I generated with openssl for this question (RSA 2048):
Private:
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCeL7K++3+vl52Q
WFC6ThgvNPlhHLlP5ylIMi/7Gy60wcCHtx8R5Hzi2j7Kx/uBXyr7SCbePS7NtqHx
meVK3VhEvYHz2uYaUNf6GqJgNNjfRymnL5ll8K8lq1wym6A7KZ+L3qLHH1oI6GfW
+uf32nUfy1PQvsatPN7aiJ4ymiaJj2LcI6Bp78CINsu56Cx9QBN9uoXqKO/7NjOz
y2GRdYdckiqCkcmGDzA4/5tJROxj21gUbxwoUR+yz2sVFGlcJycsDDJiIXiPjbVN
XgM/YfmRjdAPQQwXXj9AK5w/dp9TPq621WqYES74rIY05cba4v1kihmFbs0DHLrV
fQZB0Pu9AgMBAAECggEAGuD//nO9vpiErYJUNVQPx/W4akf3NRySZzIf9QspZI2H
qYf0P5YTonhzMwHIOrNxGkGoWRsMWOgvnF4KGC6EUSniaw1HDDGwgU8FSFOyhj4R
VddAuZGsMTps8CyBjYwFED9EaZFqOxlCi8UWpYb5X+2s0EuadtVhCMEuIGsRIU53
mfW11182YtbAI7Zqi7wg/w0yRz5rVj4ph8nbSFPgi6qtVWA9bxVxNeRTXyQDWjUU
NFQzGcRG6D/SYTnRzTndVM5TmTwEVt0hvJNA2/pMWF5XMu6B2exOpJIVVVdQ40l/
XHh33SU8+gQYY56rHzFebCa0Nuxwdk0paluv+x3CAQKBgQDKwe8QXfNlMDqrM06n
iq/NwKE7tB+1gs1nKr4qZdbI71IzgQJgIJcxbJwbuE6z6Yk8PoancEELH9mSY7EN
YGeSO3QsO1LF1vyJWgWQU1G3pfC9sh8sY6f9WYg8+JogIeJsgjwf7PXrGaBq8++Q
GBMUATzPAh/ERIqUip0nEQ2IJwKBgQDHuYgPvv77qhLc+ZNOdhaZtibeVsFx50V+
a+qR+INSTJ/CChNkoVtMg803ZQWckJBOYL/TS2Mw+ctNomUg+ImjULGT9GzqMeme
HkPpAj5pNyNVpRfVNZfmS2cwwuRyE1QbFGs3C4p7D5MKKCfl4/8MBQXtJGWrQZFr
owh2NNyHewKBgHsMmSYorlcBnwlZOOnK7AiFWBRgq0G/4SI0OXaHmYMWYp+pMqTe
AoPXMyJLh0/+ce/izlt9b6vtp2AFKmVA1XpUpJtXYVN5tocw3+GH/zbh+SlWmT6a
OFAz7s953CeWCNDrdMu3RkNoqQdfhUrAoYtpeNr0ogy9wBCH0vnrinfPAoGBALNy
U/hpz+lH1qjqKFsPqKC001ljM21msL60sU4zrbHNHKEXsnLwsvodVc3Wm2MfVDjH
nrJ2gomnde2r4hbsl6W/w70+mHkXHWKuqK97D54zJzE1IyOygmctCmr6QIzqJuAp
yWbsnKCSzrcKe0aHQkmHXdrCoAJt5/2AvwKN3jJvAoGAIaLts1F7shIZ365FXyWD
poOFtXsNRXESkcYpJ2Pn+K0fUNtdH4BtR/5Z5bsEWXBf2LbL7s15UZFZbck0KH3K
22BRgPQze7wEhMVFlIMNhF17WrOh2NTGUAVz2CYSthbYh0QSI+5XJk0AEfiQcqYk
+E4bZw/RUTY94V+ITEK6F8g=
public
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAni+yvvt/r5edkFhQuk4Y
LzT5YRy5T+cpSDIv+xsutMHAh7cfEeR84to+ysf7gV8q+0gm3j0uzbah8ZnlSt1Y
RL2B89rmGlDX+hqiYDTY30cppy+ZZfCvJatcMpugOymfi96ixx9aCOhn1vrn99p1
H8tT0L7GrTze2oieMpomiY9i3COgae/AiDbLuegsfUATfbqF6ijv+zYzs8thkXWH
XJIqgpHJhg8wOP+bSUTsY9tYFG8cKFEfss9rFRRpXCcnLAwyYiF4j421TV4DP2H5
kY3QD0EMF14/QCucP3afUz6uttVqmBEu+KyGNOXG2uL9ZIoZhW7NAxy61X0GQdD7
vQIDAQAB
What do I wrong?
CryptEncrypt returns the ciphertext in little-endian format, see CryptEncrypt( Remarks, last section). For the decryption in Java the byte array messageBytes must therefore be inverted, e.g. here.
Related
I was given following java code:
private static String key = "0123456789ABCDEF0123456789ABCDEF"; // Not real key
public static String Encrypt(String text)
{
byte[] encrypted, bytekey = hexStringToByteArray(key);
SecretKeySpec sks = new SecretKeySpec(bytekey, "AES");
try
{
Cipher cipher = Cipher.getInstance("AES");
cipher.init(1, sks, cipher.getParameters());
encrypted = cipher.doFinal(text.getBytes());
}
catch (Exception e)
{
System.out.println("Error using AES encryption with this Java instance");
e.printStackTrace();
System.exit(1);
return null;
}
String encryptedText = byteArrayToHexString(encrypted);
return encryptedText;
}
Passing Password123 into this returns 6836A38816248A0C7DD89400A997251A. I'm not looking for comments on the security of this. I'm aware. I didn't write it, I just need to duplicate it.
I have to create C# code that has the same functionality. I have tried many code snippets from all over SO and other web sites. None of them produce the same output when given a specific input.
I added some debug statements to the java code to get the following information about the algorithm:
sks.getAlgorithm(): AES (duh)
sks.getFormat(): RAW
cipher.getAlgorithm(): AES (again, duh)
cipher.getBlockSize(): 16
cipher.getParameters(): null
cipher.getIV(): null (I think this might be my primary issue)
Here is one of the C# methods I found that looked promising:
private const string key = "0123456789ABCDEF0123456789ABCDEF"; // Not real key
private static byte[] encryptionKey= new byte[16];
static void SetupKey()
{
var secretKeyBytes = Encoding.UTF8.GetBytes(key);
Array.Copy(secretKeyBytes, encryptionKey, Math.Min(encryptionKey.Length, secretKeyBytes.Length));
}
public static String Encrypt3(String secret)
{
SetupKey();
byte[] inputBytes = UTF8Encoding.UTF8.GetBytes(secret);
using (MemoryStream ms = new MemoryStream())
{
using (AesManaged cryptor = new AesManaged())
{
cryptor.Mode = CipherMode.CBC;
cryptor.Padding = PaddingMode.PKCS7;
cryptor.KeySize = 128;
cryptor.BlockSize = 128;
using (CryptoStream cs = new CryptoStream(ms, cryptor.CreateEncryptor(encryptionKey, null), CryptoStreamMode.Write))
{
cs.Write(inputBytes, 0, inputBytes.Length);
}
byte[] encryptedContent = ms.ToArray();
byte[] result = new byte[encryptedContent.Length];
System.Buffer.BlockCopy(encryptedContent, 0, result, 0, encryptedContent.Length);
return ByteArrayToHexString(result);
}
}
}
Every time I run this code, I get a different result, even though I'm passing null into the Initialization Vector(IV) parameter of cryptor.CreateEncryptor(). Is the AesManaged object using an internal IV even though I told it to use null? If I try to set the IV to null, I get an error.
What do I need to do to get the C# code to consistently return the same result as the java code?
NOTE: Both methods use HexStringToByteArray and ByteArrayToHexString. The original author of the java code, for some reason, wrote his own byte/hex converters. I recreated them in C#, but they work just like the build in functions.
I'm trying to reproduce an old encryption/decryption done in Java to a new one in Ruby one because I'm rebuilding the whole app. All this to change this encryption asap, obviously.
Here is the Java code:
public class MyClass {
private static String algo;
private static SecretKey key;
static {
algo = "AES";
String keyString = "someString";
byte[] decodedKey = Base64.getDecoder().decode(keyString);
key = new SecretKeySpec(decodedKey, 0, decodedKey.length, algo);
}
private static String decrypt(String encrypted) {
try {
Cipher cipher = Cipher.getInstance(algo);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decodedBytes = Base64.getDecoder().decode(encrypted.getBytes());
byte[] original = cipher.doFinal(decodedBytes);
return new String(original);
}
catch (Exception e) {
// Some error
return "bad";
}
}
private static String encrypt(String toEncrypt) {
try {
Cipher cipher = Cipher.getInstance(algo);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encrypted = cipher.doFinal(toEncrypt.getBytes());
byte[] encryptedValue = Base64.getEncoder().encode(encrypted);
return new String(encryptedValue);
}
catch (Exception e) {
// Some error
return "bad";
}
}
}
Java code comes from here
I have a problem with the decryption. Here is my Ruby code:
key = Digest::SHA256.digest(key)
aes = OpenSSL::Cipher.new('AES-256-CBC')
aes.decrypt
aes.key = Digest::SHA256.digest(key)
aes.update(secretdata) + aes.final
# => OpenSSL::Cipher::CipherError: bad decrypt
What am I doing wrong?
The "AES" algorithm description isn't complete; it will use a default mode of operation and padding scheme. In the provider included in the JRE this will default to ECB and PKCS#7 padding ("AES/ECB/PKCS5Padding"). This is obviously insecure as ECB is insecure, but due to applications relying on this default it cannot be changed (which is one of the reasons why having defaults was a mistake in the first place).
Furthermore, in the code you've provided there is no hashing involved. This is a good thing as a single secure hash over a key is not enough to provide a good amount of security. Storing a key in a string is almost as bad, but not quite. Instead however the key is base 64 encoded.
So you have to switch to 'AES-256-ECB' and remove the double hashing of the key, replacing it with base 64 decoding instead.
It was not that easy but here is my way to do it.
class ManualEncryption
class << self
attr_accessor :configuration
def config
#configuration ||= Configuration.new
end
def aes
return #aes if #aes
#aes = OpenSSL::Cipher.new(config.algo) # "AES-256-ECB"
#aes
end
def decodedKey
return #decodedKey if #decodedKey
#decodedKey = Base64.decode64(config.key_string) # "mySecretString"
end
def configure
yield(config)
raise 'Algo not specified' unless config.algo
raise 'key not specified' unless config.key_string
end
def encrypt(value)
raise 'No configuration done' unless config.algo && config.key_string
aes_perform('encrypt', value)
end
def decrypt(value)
raise 'No configuration done' unless config.algo && config.key_string
return value unless value
aes_perform('decrypt', value)
end
def aes_perform(status, value)
aes.reset
if status.eql?('encrypt')
aes.encrypt
aes.key = decodedKey
aes_val = aes.update(value) + aes.final
Base64::encode64(aes_val)
else
aes.decrypt
aes.key = decodedKey
decoded_value = Base64::decode64(value)
aes.update(decoded_value) + aes.final
end
end
end
class Configuration
attr_accessor :algo, :key_string
attr_reader :aes
end
end
Note: I still have a problem with the encryption. It creates \n inside my encrypted value and I don't know why. I'm working on it.
The encrypted text is done in JAVA (which we have no JAVA background at all)
The decryption will be in C#, and here is the code
public static string DecryptString(string Message, string Passphrase)
{
byte[] Results;
UTF8Encoding UTF8 = new UTF8Encoding();
MD5CryptoServiceProvider HashProvider = new MD5CryptoServiceProvider();
byte[] TDESKey = HashProvider.ComputeHash(UTF8.GetBytes(Passphrase));
// byte[] TDESKey = UTF8.GetBytes(Passphrase);
TripleDESCryptoServiceProvider TDESAlgorithm = new TripleDESCryptoServiceProvider();
TDESAlgorithm.Key = TDESKey;
// TDESAlgorithm.Mode = CipherMode.CTS;
TDESAlgorithm.Padding = PaddingMode.Zeros;
byte[] DataToDecrypt = Convert.FromBase64String(Message);
try
{
ICryptoTransform Decryptor = TDESAlgorithm.CreateDecryptor();
Results = Decryptor.TransformFinalBlock(DataToDecrypt, 0, DataToDecrypt.Length);
}
finally
{
TDESAlgorithm.Clear();
HashProvider.Clear();
}
return Encoding.UTF8.GetString(Results);
}
Encrypted Java code is
public String encryptData(String privateKey, String rawData)
{
Cipher cipher = null;
try
{
cipher = Cipher.getInstance(DESEDE_ENCRYPTION_SCHEME);
cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(privateKey));
byte[] plainText = rawData.getBytes(UNICODE_FORMAT);
byte[] encryptedText = cipher.doFinal(plainText);
return new String(Base64.encodeBase64(encryptedText));
}
}
However, when tried to decrypt, got the error message: BAD DATA
Where am I missing here?
You are not using MD5 in Java, so you should not be using it in your .NET for computing the hash.
Your key should have been generated using a specific encoding and same you should use in .NET.
Please note, there is some fundamental difference in java KeySpec and the Key being used for TripleDESCryptoServiceProvider. As mentioned by Microsfot https://msdn.microsoft.com/en-us/library/system.security.cryptography.tripledescryptoserviceprovider.aspx
Triple DES only supports "key lengths from 128 bits to 192 bits in increments of 64 bits"
So you need to convert your key appropriately before assigning. To do this you can use the Array.Resize method as following.
byte[] TDESKey = Encoding.UTF8.GetBytes(Passphrase);
System.Array.Resize(ref TDESKey , 192 / 8);
Hope this will help.
I'm developing an android app and getting this public key from the server of the company that I work with:
PFJTQUtleVZhbHVlPjxNb2R1bHVzPnZOcFhkRWVOTU5pZDhuOTlUekRGMVo4MDNvTEdRSzlqWnNFODlDd2tiS29GV0tGZmt2QTZKODBNWHhPZnhqbFZIYU8vYWM4YUpMc1AxWVR1RFNHVis3VExQL0puVVpyNlJQQTdpbFlmMitVWExiS0U2ZW1RYzBKdXlOaVArL0FTMGZmKzYwSnZQekhYeEdQQnVIbWtTcmRqdEtFV0JCZXJzWWNuQVJyT2ZSYz08L01vZHVsdXM+PEV4cG9uZW50PkFRQUI8L0V4cG9uZW50PjwvUlNBS2V5VmFsdWU+
the server is windows with IIS 7.5.
base64decode it give me this XML
<RSAKeyValue><Modulus>vNpXdEeNMNid8n99TzDF1Z803oLGQK9jZsE89CwkbKoFWKFfkvA6J80MXxOfxjlVHaO/ac8aJLsP1YTuDSGV+7TLP/JnUZr6RPA7ilYf2+UXLbKE6emQc0JuyNiP+/AS0ff+60JvPzHXxGPBuHmkSrdjtKEWBBersYcnARrOfRc=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>
Extracting the Modulu and the Exponent from this xml, base64decode them and making the spec for the public key object:
PublicKey pbKey = null;
XMLParser parser = new XMLParser();
Document doc = parser.getDomElement(publicKeyString);
Element rsakeyvalue = doc.getDocumentElement();
String modulusBase64 = parser.getValue(rsakeyvalue, "Modulus");
byte[] modulus = Base64.decode(modulusBase64, 0);
String exponentBase64 = parser.getValue(rsakeyvalue, "Exponent");
byte[] exponent = Base64.decode(exponentBase64, 0);
BigInteger modBigInteger = new BigInteger(1,modulus);
BigInteger exBigInteger = new BigInteger(1,exponent);
RSAPublicKeySpec spec = new RSAPublicKeySpec(modBigInteger, exBigInteger);
try {
KeyFactory factory = KeyFactory.getInstance("RSA");
pbKey = factory.generatePublic(spec);
} catch (Exception e) {
e.printStackTrace();
}
Creating the cipher and adding the plain text to encrypt with:
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC");
byte[] keyBytes = Base64.decode(this.publicKey, 0);
/* the strToPublicKey is the previews code block */
PublicKey publickey = strToPublicKey(new String(keyBytes));
cipher.init( Cipher.ENCRYPT_MODE , publickey );
// Base 64 encode removed.
//byte[] encryptedBytes = Base64.encode( cipher.doFinal(plainText.getBytes()), 0 );
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes());
everything here is working but the server don't accept it,
I'm base64 encode it and sending it as byte array.
the server admin saying it's too long, supposed to be 128 bit but it's 174 bit...
what I'm doing wrong?
How its called this key? RSA XML is correct? or it's got other name?
I can also get PEM string from the server if someone have an idea how to do it with it.
Edit:
i forgot to put here an important part, i'm sending the data to the server as byte array, this is how i make the string of it:
public static int unsignedToBytes(byte b) {
return b & 0xFF;
}
StringBuilder byteArrayString = new StringBuilder();
int bytesLength = bytes.length;
int bytesCounter = 0;
for (byte aByte : bytes) {
bytesCounter++;
byteArrayString.append(unsignedToBytes(aByte));
if(bytesCounter < bytesLength){
byteArrayString.append(",");
}
}
SOLVED! - the solution:
the bytes that the string builder used are signed so i use this function unsignedToBytes() to make them unsigned and i removed the base64Encription on the encrypt() function.
this was the problem, i hope it will help anyone else.
Your variable naming is bad. encryptedBytes contains not the encrypted bytes but the encrypted bytes in base64 encoding.
This is the reason you do not get the expected result.
The length of the encrypted data before applying Base64 encoding is 128 byte.
Afterwards the length is 172 bytes.
I need to access some data that used PHP encryption. The PHP encryption is like this.
base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($cipher), $text, MCRYPT_MODE_ECB));
As value of $text they pass the time() function value which will be different each time that the method is called in. I have implemented this in Java. Like this,
public static String md5(String string) {
byte[] hash;
try {
hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8"));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Huh, MD5 should be supported?", e);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Huh, UTF-8 should be supported?", e);
}
StringBuilder hex = new StringBuilder(hash.length * 2);
for (byte b : hash) {
int i = (b & 0xFF);
if (i < 0x10) hex.append('0');
hex.append(Integer.toHexString(i));
}
return hex.toString();
}
public static byte[] rijndael_256(String text, byte[] givenKey) throws DataLengthException, IllegalStateException, InvalidCipherTextException, IOException{
final int keysize;
if (givenKey.length <= 192 / Byte.SIZE) {
keysize = 192;
} else {
keysize = 256;
}
byte[] keyData = new byte[keysize / Byte.SIZE];
System.arraycopy(givenKey, 0, keyData, 0, Math.min(givenKey.length, keyData.length));
KeyParameter key = new KeyParameter(keyData);
BlockCipher rijndael = new RijndaelEngine(256);
ZeroBytePadding c = new ZeroBytePadding();
PaddedBufferedBlockCipher pbbc = new PaddedBufferedBlockCipher(rijndael, c);
pbbc.init(true, key);
byte[] plaintext = text.getBytes(Charset.forName("UTF8"));
byte[] ciphertext = new byte[pbbc.getOutputSize(plaintext.length)];
int offset = 0;
offset += pbbc.processBytes(plaintext, 0, plaintext.length, ciphertext, offset);
offset += pbbc.doFinal(ciphertext, offset);
return ciphertext;
}
public static String encrypt(String text, String secretKey) throws Exception {
byte[] givenKey = String.valueOf(md5(secretKey)).getBytes(Charset.forName("ASCII"));
byte[] encrypted = rijndael_256(text,givenKey);
return new String(Base64.encodeBase64(encrypted));
}
I have referred this answer when creating MCRYPT_RIJNDAEL_256 method."
Encryption in Android equivalent to php's MCRYPT_RIJNDAEL_256
"I have used apache codec for Base64.Here's how I call the encryption function,
long time= System.currentTimeMillis()/1000;
String encryptedTime = EncryptionUtils.encrypt(String.valueOf(time), secretkey);
The problem is sometimes the output is not similar to PHP but sometimes it works fine.
I think that my MCRYPT_RIJNDAEL_256 method is unreliable.
I want to know where I went wrong and find a reliable method so that I can always get similar encrypted string as to PHP.
The problem is likely to be the ZeroBytePadding. The one of Bouncy always adds/removes at least one byte with value zero (a la PKCS5Padding, 1 to 16 bytes of padding) but the one of PHP only pads until the first block boundary is encountered (0 to 15 bytes of padding). I've discussed this with David of the legion of Bouncy Castle, but the PHP zero byte padding is an extremely ill fit for the way Bouncy does padding, so currently you'll have to do this yourself, and use the cipher without padding.
Of course, as a real solution, rewrite the PHP part to use AES (MCRYPT_RIJNDAEL_128), CBC mode encryption, HMAC authentication, a real Password Based Key Derivation Function (PBKDF, e.g. PBKDF2 or bcrypt) and PKCS#7 compatible padding instead of this insecure, incompatible code. Alternatively, go for OpenSSL compatibility or a known secure container format.