1

I hava a specific problem related to file decryption. I found a program in Java, that decrypts specific file (this is game save file from a game you can find example of that file here: https://www.adrive.com/public/6DBShx/game.sii ) and I want to rewrite this program to C# - so I can integrate it to totaly different program I wrote in C#. This is my first time with encrypted files, so program I made isn't working, and actually I'm not sure how to debug it. Program I wrote has fixed paths to files - this is temporary solution.

Original Java program:

package scsc;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.security.Permission;
import java.security.PermissionCollection;
import java.util.Map;
import java.util.zip.InflaterInputStream;

/*
 * Published under the Do What the Fuck You Want to Public License (       http://www.wtfpl.net/ )
 */
public class ScsC {
private static byte[] AES_KEY = new byte[]{
        (byte) 0x2a, (byte) 0x5f, (byte) 0xcb, (byte) 0x17,
        (byte) 0x91, (byte) 0xd2, (byte) 0x2f, (byte) 0xb6,
        (byte) 0x02, (byte) 0x45, (byte) 0xb3, (byte) 0xd8,
        (byte) 0x36, (byte) 0x9e, (byte) 0xd0, (byte) 0xb2,
        (byte) 0xc2, (byte) 0x73, (byte) 0x71, (byte) 0x56,
        (byte) 0x3f, (byte) 0xbf, (byte) 0x1f, (byte) 0x3c,
        (byte) 0x9e, (byte) 0xdf, (byte) 0x6b, (byte) 0x11,
        (byte) 0x82, (byte) 0x5a, (byte) 0x5d, (byte) 0x0a,
};

public static void main(String[] args) throws Exception {
    if (args.length < 1) {
        System.out.println("ERROR: expecting at least one file.");
    } else {
        removeCryptographyRestrictions();
        for (String filename : args) {
            decrypt(filename);
        }
    }
}

private static void decrypt(String filename) throws Exception {
    File scsc = new File(filename);
    if (!scsc.isFile() || !scsc.canWrite()) {
        throw new IllegalArgumentException(filename + " is not a writable file.");
    }
    boolean encrypted = isEncrypted(scsc);
    if (!encrypted) {
        System.out.println(scsc + " does not seem to be encrypted.");
    } else {
        File decrypted = decrypt(scsc);
        Files.copy(decrypted.toPath(), scsc.toPath(), StandardCopyOption.REPLACE_EXISTING);
        System.out.println("decrypted: " + scsc);
    }
}

private static boolean isEncrypted(File file) throws Exception {
    byte[] header = new byte[4];
    FileInputStream fis = new FileInputStream(file);
    if (fis.read(header) != header.length) {
        throw new RuntimeException("could not read header of " + file);
    }
    fis.close();
    String headerAsString = new String(header, Charset.forName("UTF-8"));
    return "ScsC".equals(headerAsString);
}

private static File decrypt(File input) throws Exception {
    File out = File.createTempFile("scsc-", ".tmp");
    out.deleteOnExit();

    byte[] data = new byte[(int) (input.length())];
    FileInputStream fis = new FileInputStream(input);
    if (fis.read(data) != data.length) {
        throw new RuntimeException("Could not read " + input + " into memory");
    }
    fis.close();

    byte[] cipherText = new byte[data.length - 0x38];
    byte[] iv = new byte[0x10];
    System.arraycopy(data, 0x38, cipherText, 0, cipherText.length);
    System.arraycopy(data, 0x24, iv, 0, iv.length);
    byte[] decrypted = decrypt(cipherText, AES_KEY, iv);

    ByteArrayInputStream bis = new ByteArrayInputStream(decrypted);
    InflaterInputStream iis = new InflaterInputStream(bis);
    InputStreamReader ir = new InputStreamReader(iis);
    BufferedReader br = new BufferedReader(ir);

    FileOutputStream fos = new FileOutputStream(out);
    OutputStreamWriter osw = new OutputStreamWriter(fos);
    PrintWriter pw = new PrintWriter(osw);

    for (String line = br.readLine(); line != null; line = br.readLine()) {
        pw.println(line);
    }
    pw.close();
    br.close();

    return out;
}

private static byte[] decrypt(byte[] cipherText, byte[] keyBytes, byte[] iv) throws Exception {
    Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
    SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
    cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
    return cipher.doFinal(cipherText);
}

private static void removeCryptographyRestrictions() throws Exception {
    // taken from http://stackoverflow.com/questions/1179672/unlimited-strength-jce-policy-files
    final Class<?> jceSecurity = Class.forName("javax.crypto.JceSecurity");
    final Class<?> cryptoPermissions = Class.forName("javax.crypto.CryptoPermissions");
    final Class<?> cryptoAllPermission = Class.forName("javax.crypto.CryptoAllPermission");

    final Field isRestrictedField = jceSecurity.getDeclaredField("isRestricted");
    isRestrictedField.setAccessible(true);
    isRestrictedField.set(null, false);

    final Field defaultPolicyField = jceSecurity.getDeclaredField("defaultPolicy");
    defaultPolicyField.setAccessible(true);
    final PermissionCollection defaultPolicy = (PermissionCollection) defaultPolicyField.get(null);

    final Field perms = cryptoPermissions.getDeclaredField("perms");
    perms.setAccessible(true);
    ((Map<?, ?>) perms.get(defaultPolicy)).clear();

    final Field instance = cryptoAllPermission.getDeclaredField("INSTANCE");
    instance.setAccessible(true);
    defaultPolicy.add((Permission) instance.get(null));
}
}

My code, that I wrote in C# produce seems to work, but output is not as expected... expected results should be readable file (link: https://www.adrive.com/public/E3rsfv/game.sii_decrypted_by_java )

first few lines of decrypted file:

SiiNunit
{
economy : _nameless.0357.B5D0 {
 bank: _nameless.0371.CD48
 player: _nameless.0369.3070
 companies: 811
 companies[0]: company.volatile.trameri.esbjerg
 companies[1]: company.volatile.bcp.turku
 companies[2]: company.volatile.itcc.liege
 companies[3]: company.volatile.tree_et.geneve
 companies[4]: company.volatile.tree_et.arhus
 companies[5]: company.volatile.itcc.jonkoping
 companies[6]: company.volatile.kaarfor.poznan
 companies[7]: company.volatile.fcp.milano
 companies[8]: company.volatile.posped.swansea
 companies[9]: company.volatile.tradeaux.lille
 companies[10]: company.volatile.euroacres.osnabruck

decrypted by c# - i cannot put to many links here :(

first few lines:

     xڬ�M�$������C��4fcm�o��#=-     ��rLV������̪[���<�8�J��F����t��C������:�>������>Ƿ_��Ƿ ��m��~|�9�T����ߦ?�����*[�����X��} ���=����S�������9��Q����?�����~����|���?? O���������ӳx�Y<<����uz�PoAw����w���������������~sz�� ������z|��Wf��ٜ�O�6�/�;I�m���[-����}�������c��T��| ��D��K�8S�ۯ�q�x�~�y�8lN_���)7�������ux��xг���@}l�����15} x�������c��#Ul�>>���͛|�T����>�q�����py�! ����X�'�tW����!��ow�w��۸c�Ə��^κ �ŗ�{�I�P���7���9<| ��O�}�������6_�ؾ=��}�����{RVħ�9��?��i�|A�awz#;���w�Km� %�3a�"~ݜ��9y4�����r�6����ގ_�/ �1<ݍqL����w�����=�l~���H\�y4J������(gU(k��m�S�S���es8��3m Ű,�M��_��~��w� ��S- ��Y|z�KP�P�����:ֆ������G�q��v�&�QLp6��C}<? �G5�cY�����q��)���q�D�|ڪε�i�����XI��>Ӕ_:c�
 ��~�<�1��h���=���{8m��G�8X��ٞҚB�6�Oۅ�Jy<�>����  ����b�Mu�l�v�W[u
�]�V'�����帗�Cv��H�C��9   ����t�e��V�������^�Cy�߁oIñ���r�����R΃��H���C{�����ӏ�m ���

and the class that create this in c#:

public partial class Form1 : Form
    {
        string file_original;
        byte[] file_orig_bytes;

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            byte[] AES_KEY = new byte[]{
            (byte) 0x2a, (byte) 0x5f, (byte) 0xcb, (byte) 0x17,
            (byte) 0x91, (byte) 0xd2, (byte) 0x2f, (byte) 0xb6,
            (byte) 0x02, (byte) 0x45, (byte) 0xb3, (byte) 0xd8,
            (byte) 0x36, (byte) 0x9e, (byte) 0xd0, (byte) 0xb2,
            (byte) 0xc2, (byte) 0x73, (byte) 0x71, (byte) 0x56,
            (byte) 0x3f, (byte) 0xbf, (byte) 0x1f, (byte) 0x3c,
            (byte) 0x9e, (byte) 0xdf, (byte) 0x6b, (byte) 0x11,
            (byte) 0x82, (byte) 0x5a, (byte) 0x5d, (byte) 0x0a,
            };

            string path = "game.sii";
            load_file_to_memory(path);
            //decrypt_file(AES_KEY);
            byte[] iv = new byte[0x10];
            byte[] cipherText = new byte[file_orig_bytes.Length - 0x38];
            Array.Copy(file_orig_bytes, 0x38, cipherText, 0, cipherText.Length);
            Array.Copy(file_orig_bytes, 0x24, iv, 0, iv.Length);

            string new_file = DecryptStringFromBytes_Aes(cipherText, AES_KEY, iv);
            int a;

        }
        private void load_file_to_memory(string path)
        {
            file_original = File.ReadAllText(path);
            file_orig_bytes = File.ReadAllBytes(path);
            int i;
        }

        static string DecryptStringFromBytes_Aes(byte[] cipherText, byte[] Key
, byte[] IV)
        {
            // Check arguments.
            if (cipherText == null || cipherText.Length <= 0)
                throw new ArgumentNullException("cipherText");
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (IV == null || IV.Length <= 0)
                throw new ArgumentNullException("Key");

            // Declare the string used to hold
            // the decrypted text.
            string plaintext = null;

            // Create an Aes object
            // with the specified key and IV.
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = Key;
                aesAlg.IV = IV;

                // Create a decrytor to perform the stream transform.
                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

                // Create the streams used for decryption.
                using (MemoryStream msDecrypt = new MemoryStream(cipherText))
                {
                    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                        {

                            // Read the decrypted bytes from the decrypting stream
                            // and place them in a string.
                            plaintext = srDecrypt.ReadToEnd();
                        }
                    }
                }

            }

            return plaintext;

        }

Any help on this? Thanks

Greg K.
  • 15
  • 5
  • 1
    Why you don't use the framework existing class https://msdn.microsoft.com/en-us/library/system.security.cryptography.aes(v=vs.110).aspx ? – bdn02 Jan 28 '16 at 20:02
  • 1
    @bdn02 AES and Rijndael are synonymous for the default block size of 128. – Artjom B. Jan 28 '16 at 20:32
  • I've rewrite it with the default example - it works better - no empty string. Code is attached in answer (should I edit original post instead?), but still don't have original results. return string seems to be still encrypted... – Greg K. Jan 28 '16 at 20:42
  • removed my answer, and rewrite first post with new, probably better version. Add some outputs, some full in files to download, but reputation does not allow me to send more links, yet... – Greg K. Jan 28 '16 at 21:14

2 Answers2

1

This is my version and seems works well. The problem on your last code is that the unencrypted content in original java version was manupulated with "InflaterInputStream" class, in c# i found this class in sharpziplib.

public class Test2
{
    static string file_original;
    static byte[] file_orig_bytes;

    static void Main(string[] args)
    {
        byte[] AES_KEY = new byte[]{
            (byte) 0x2a, (byte) 0x5f, (byte) 0xcb, (byte) 0x17,
            (byte) 0x91, (byte) 0xd2, (byte) 0x2f, (byte) 0xb6,
            (byte) 0x02, (byte) 0x45, (byte) 0xb3, (byte) 0xd8,
            (byte) 0x36, (byte) 0x9e, (byte) 0xd0, (byte) 0xb2,
            (byte) 0xc2, (byte) 0x73, (byte) 0x71, (byte) 0x56,
            (byte) 0x3f, (byte) 0xbf, (byte) 0x1f, (byte) 0x3c,
            (byte) 0x9e, (byte) 0xdf, (byte) 0x6b, (byte) 0x11,
            (byte) 0x82, (byte) 0x5a, (byte) 0x5d, (byte) 0x0a,
            };

        string path = @"C:\temp_10\game.sii";
        load_file_to_memory(path);
        //decrypt_file(AES_KEY);
        byte[] iv = new byte[0x10];
        byte[] cipherText = new byte[file_orig_bytes.Length - 0x38];
        Array.Copy(file_orig_bytes, 0x38, cipherText, 0, cipherText.Length);
        Array.Copy(file_orig_bytes, 0x24, iv, 0, iv.Length);

        byte[] decoded_binary_content = AESDecrypt(cipherText, AES_KEY, iv);

        string decoded_string_content = "";
        using (MemoryStream ms = new MemoryStream(decoded_binary_content))
        using (ICSharpCode.SharpZipLib.Zip.Compression.Streams.InflaterInputStream iis = new ICSharpCode.SharpZipLib.Zip.Compression.Streams.InflaterInputStream(ms))
        using (StreamReader sr = new StreamReader(iis))
            decoded_string_content = sr.ReadToEnd();
    }

    private static void load_file_to_memory(string path)
    {
        file_original = File.ReadAllText(path);
        file_orig_bytes = File.ReadAllBytes(path);
    }

    //http://lamahashim.blogspot.it/2009/08/encyptiondecryption-in-c-and-java.html
    static byte[] AESDecrypt(byte[] encryptedData, byte[] keyBytes, byte[] iv)
    {
        RijndaelManaged rijndaelCipher = new RijndaelManaged();
        rijndaelCipher.Mode = CipherMode.CBC;
        rijndaelCipher.Padding = PaddingMode.None;
        rijndaelCipher.IV = iv;

        rijndaelCipher.KeySize = 0x80;
        rijndaelCipher.BlockSize = 0x80;
        rijndaelCipher.Key = keyBytes;
        rijndaelCipher.IV = iv;
        byte[] plainText = rijndaelCipher.CreateDecryptor().TransformFinalBlock(encryptedData, 0, encryptedData.Length);
        return plainText;
    }
}

I have copied the decrypt function from the url shown in the code

bdn02
  • 1,500
  • 9
  • 15
  • I've reviewed the code and the sharpziplib library. As I understand, the 'garbage' content I received at the beginng was acctually compressed file? Is that right? – Greg K. Jan 29 '16 at 08:55
  • Yes, i think so. If you find the java class definition for 'InflaterInputStream' you can read: 'This class implements a stream filter for uncompressing data in the "deflate" compression format. It is also used as the basis for other decompression filters, such as GZIPInputStream' – bdn02 Jan 29 '16 at 09:05
0

Having developed an interest in reading SII files, I stumbled across this stack question after coming across the same Java program, and with the help of bdn02's answer I got an app up and running and made some changes.

This version uses the DeflateStream class from the System.IO.Compression namespace, instead of SharpZipLib, and skips the first two bytes of the compressed contents as these bytes contain compression information and flags for the zlib specification (RFC 1950) whereas this code uses the deflate specification (RFC 1951).

Posting this answer for anybody else who comes across this question in the future, and who doesn't want to rely on SharpZipLib or any third party packages.

class Program
{
    private static byte[] AES_Key = { 42, 95, 203, 23, 145, 210, 47, 182, 2, 69, 179, 216, 54, 158, 208, 178, 194, 115, 113, 86, 63, 191, 31, 60, 158, 223, 107, 17, 130, 90, 93, 10 };

    static void Main(string[] args)
    {
        // Get bytes of AES Key, read contents
        var contentsBytes = File.ReadAllBytes("profile.sii");

        // Create IV & Cipher test byte arrays
        var iv = new byte[0x10];
        var cipherText = new byte[contentsBytes.Length - 0x38];

        // Copy contents bytes to cipherText & IV arrays
        Array.Copy(contentsBytes, 56, cipherText, 0, cipherText.Length);
        Array.Copy(contentsBytes, 36, iv, 0, iv.Length);

        // Create AES Crypto Service Provider with our Key & Vector.
        var cryptoServiceProvider = new AesCryptoServiceProvider
        {
            Mode = CipherMode.CBC,
            Padding = PaddingMode.None,
            KeySize = 0x80,
            BlockSize = 0x80,
            Key = AES_Key,
            IV = iv
        };

        // Create decryptor and get plain text of encrypted contents.
        var decryptor = cryptoServiceProvider.CreateDecryptor();
        var plainText = decryptor.TransformFinalBlock(cipherText, 0, cipherText.Length);

        string decompressedContents;
        // Load into Memory Stream and skip the first 2 bytes.
        // Use a Deflate stream to decompress the contents
        // Read the deflated contents via our StreamReader
        using (var memStream = new MemoryStream(plainText, 2, plainText.Length - 2))
        using (var iis = new DeflateStream(memStream, CompressionMode.Decompress))
        using (var streamReader = new StreamReader(iis))
        {
            decompressedContents = streamReader.ReadToEnd();
        }
    }
}
ColinM
  • 2,622
  • 17
  • 29