Viewed   74 times

I am searching for a way to encrypt a .txt file into a zip, but in a secure password protected way. My goal is to email this file to me, without anyone being able to read the content of the attachment.

Does anybody know an easy, and above all, secure way to accomplish this ? I can create zip archives, but I do not know how to encrypt them, or, how secure this is.

 Answers

2

Note: this answer recommends a cryptographic method that is known insecure, even with good password. Please see link from comments and the Winzip QA on AES. Support for in-php AES zip encryption arrives with php 7.2 (and libzip 1.2.0), which means this answer will soon be outdated too. Until then see this answer for how to call out to 7z instead of the zip command, which supports winzip's AES encryption.

You can use this:

<?php echo system('zip -P pass file.zip file.txt'); ?>

Where pass is the password, and file.txt will be zipped into file.zip. This should work on Windows and Linux, you just need to get a free version of zip for Windows ( http://www.info-zip.org/Zip.html#Win32 )

This kind of security can be broken by brute force attacks, dictionary attacks and etc. But it's not that easy, specially if you chose a long and hard to guess password.

Wednesday, September 7, 2022
4

You should check which error code gives open (http://www.php.net/manual/en/ziparchive.open.php), that will give you some help.

Error codes are this:

    ZIPARCHIVE::ER_EXISTS -10
    ZIPARCHIVE::ER_INCONS - 21
    ZIPARCHIVE::ER_INVAL - 18
    ZIPARCHIVE::ER_MEMORY - 14
    ZIPARCHIVE::ER_NOENT - 9
    ZIPARCHIVE::ER_NOZIP - 19
    ZIPARCHIVE::ER_OPEN - 11
    ZIPARCHIVE::ER_READ - 5
    ZIPARCHIVE::ER_SEEK - 4
Monday, September 12, 2022
 
doxick
 
3

This should do it.

public static byte[] encrypt(byte[] decrypted, byte[] keyvalue) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException{
    MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
    byte[] key = sha256.digest(keyvalue);

    MessageDigest md5 = MessageDigest.getInstance("MD5");
    byte[] checksum = md5.digest(decrypted);

    //The length of the value to encrypt must be a multiple of 16.
    byte[] decryptedAndChecksum = new byte[(decrypted.length + md5.getDigestLength() + 15) / 16 * 16];
    System.arraycopy(decrypted, 0, decryptedAndChecksum, 0, decrypted.length);
    System.arraycopy(checksum, 0, decryptedAndChecksum, decrypted.length, checksum.length);
    //The remaining bytes of decryptedAndChecksum stay as 0 (default byte value) because PHP pads with 0's.

    SecureRandom rnd = new SecureRandom();
    byte[] iv = new byte[16];
    rnd.nextBytes(iv);
    IvParameterSpec ivSpec = new IvParameterSpec(iv);

    Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
    cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), ivSpec);
    byte[] encrypted = Base64.encodeBase64(cipher.doFinal(decryptedAndChecksum));

    byte[] ivBase64 = Base64.encodeBase64String(iv).substring(0, 22).getBytes();
    byte[] output = new byte[encrypted.length + ivBase64.length];
    System.arraycopy(ivBase64, 0, output, 0, ivBase64.length);
    System.arraycopy(encrypted, 0, output, ivBase64.length, encrypted.length);
    return output;
}

The equivalent of MCRYPT_RIJNDAEL_128 and MCRYPT_MODE_CBC in java is AES/CBC/NoPadding. You also need a utility for Base64 encoding, the above code uses Base64 from the Apache Codec library.

Also, because the encryption key is 256 bits, you'll need the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files. These can be downloaded from Oracle's website.

Finally, do heed ntoskrnl's warning. This encryption really could be better, don't copy-paste from the PHP manual.

Friday, September 16, 2022
 
aoz
 
aoz
2

If you head on over to CodePlex and grab the PowerShell Community Extensions, you can use their write-zip cmdlet.

Since

CodePlex is in read-only mode in preparation for shutdown

you can go to PowerShell Gallery.

Saturday, August 13, 2022
 
jleft
 
3

A couple of thanks should go out before I answer my own question. Thanks to Dave Boyer (Mister Dai), Jason Dean and Jason Delmore for their help.

As Leigh has suggested I had to make use of Bouncy Castle, the light weight API and the Rijndael cipher engine there in.

I ended up with a function to create an rijndael cipher and functions to encrypt and decrypt a string with a key and ivsalt.

<cfcomponent displayname="Bounce Castle Encryption Component" hint="This provides bouncy castle encryption services" output="false">

<cffunction name="createRijndaelBlockCipher" access="private">
    <cfargument name="key" type="string" required="true" >
    <cfargument name="ivSalt" type="string" required="true" >
    <cfargument name="bEncrypt" type="boolean" required="false" default="1">
    <cfargument name="blocksize" type="numeric" required="false" default=256>
    <cfscript>
    // Create a block cipher for Rijndael
    var cryptEngine = createObject("java", "org.bouncycastle.crypto.engines.RijndaelEngine").init(arguments.blocksize);

    // Create a Block Cipher in CBC mode
    var blockCipher = createObject("java", "org.bouncycastle.crypto.modes.CBCBlockCipher").init(cryptEngine);

    // Create Padding - Zero Byte Padding is apparently PHP compatible.
    var zbPadding = CreateObject('java', 'org.bouncycastle.crypto.paddings.ZeroBytePadding').init();

    // Create a JCE Cipher from the Block Cipher
    var cipher = createObject("java", "org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher").init(blockCipher,zbPadding);

    // Create the key params for the cipher     
    var binkey = binarydecode(arguments.key,"hex");
    var keyParams = createObject("java", "org.bouncycastle.crypto.params.KeyParameter").init(BinKey);

    var binIVSalt = Binarydecode(ivSalt,"hex");
    var ivParams = createObject("java", "org.bouncycastle.crypto.params.ParametersWithIV").init(keyParams, binIVSalt);

    cipher.init(javaCast("boolean",arguments.bEncrypt),ivParams);

    return cipher;
    </cfscript>
</cffunction>

<cffunction name="doEncrypt" access="public" returntype="string">
    <cfargument name="message" type="string" required="true">
    <cfargument name="key" type="string" required="true">
    <cfargument name="ivSalt" type="string" required="true">

    <cfscript>
    var cipher = createRijndaelBlockCipher(key=arguments.key,ivSalt=arguments.ivSalt);
    var byteMessage = arguments.message.getBytes();
    var outArray = getByteArray(cipher.getOutputSize(arrayLen(byteMessage)));
    var bufferLength = cipher.processBytes(byteMessage, 0, arrayLen(byteMessage), outArray, 0);
    var cipherText = cipher.doFinal(outArray,bufferLength);

    return toBase64(outArray);
    </cfscript>
</cffunction>


<cffunction name="doDecrypt" access="public" returntype="string">
    <cfargument name="message" type="string" required="true">
    <cfargument name="key" type="string" required="true">
    <cfargument name="ivSalt" type="string" required="true">

    <cfscript>
    var cipher = createRijndaelBlockCipher(key=arguments.key,ivSalt=arguments.ivSalt,bEncrypt=false);
    var byteMessage = toBinary(arguments.message);
    var outArray = getByteArray(cipher.getOutputSize(arrayLen(byteMessage)));
    var bufferLength = cipher.processBytes(byteMessage, 0, arrayLen(byteMessage), outArray, 0);
    var originalText = cipher.doFinal(outArray,bufferLength);

    return createObject("java", "java.lang.String").init(outArray);
    </cfscript>
</cffunction>

<cfscript>
function getByteArray(someLength)
{
    byteClass = createObject("java", "java.lang.Byte").TYPE;
    return createObject("java","java.lang.reflect.Array").newInstance(byteClass, someLength);
}
</cfscript>

</cfcomponent>

The doEncrypt and doDecrypt functions are publically visible, but not the function that creates the rijndael cipher. The encryption and decryption functions take a string, key and ivSalt returning an encrypted or decrypted string respectively.

The createRijndaelBlockCipher takes a key, ivSalt, a boolean to state whether the cipher will be used to encrypt or decrypt and the block size, although the block size is defaulted to 256 bits. The function is fairly well commented so it should make sense.

The UDF at the bottom (special thanks to Jason Delmore for that nugget) ensures that ColdFusion correctly creates a byte array for the decryption. Some other ways of creating byte arrays just don't work or end up with inconsistent results in decryption or throw pad buffer corrupt errors.

That's about it really. It took far too much effort, when the standard AES encryption uses 128bit blocks and 128 Bit Keys are for classified up to SECRET, 192-bit or higher for TOP-SECRET. 256bit blocks and 256bit keys are just a bit over the top. Just because you can doesn't mean you should.

Please do remember that MCRYPT_RIJNDAEL_256 is the block size and not the encryption level. The encryption level is set by the strength of key that you pass into mcrypt_encrypt and increasing the block size does not increase the encryption strength.

Wednesday, November 16, 2022
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :