【问题标题】:How to encrypt a file in .net and decrypt in android如何在.net中加密文件并在android中解密
【发布时间】:2015-11-26 16:24:33
【问题描述】:

我一直在做一个项目,我们必须将 pdf 安全地保存在 android sd 卡中。 我们希望它在 .net 中加密,因为它必须通过 API 传输。 我已经在 .NET 中实现但无法在 android 中解密。

加密文件的代码

public static void EncryptFile(string inputFile, string outputFile)
    {
        try
        {
            string password = @"myKey123"; // Your Key Here
            UnicodeEncoding UE = new UnicodeEncoding();
            byte[] key = UE.GetBytes(password);

            string cryptFile = outputFile;
            FileStream fsCrypt = new FileStream(cryptFile, FileMode.Create);

            RijndaelManaged RMCrypto = new RijndaelManaged();
            RMCrypto.Mode = CipherMode.CBC; //remember this parameter
            RMCrypto.Padding = PaddingMode.PKCS7;  //remember this parameter
            RMCrypto.KeySize = 0x80;
            RMCrypto.BlockSize = 0x80;
            CryptoStream cs = new CryptoStream(fsCrypt,
                RMCrypto.CreateEncryptor(key, key),
                CryptoStreamMode.Write);

            FileStream fsIn = new FileStream(inputFile, FileMode.Open);

            int data; 
            while ((data = fsIn.ReadByte()) != -1)
            {
                cs.WriteByte((byte)data);
            }

            fsIn.Close();
            cs.Close();
            fsCrypt.Close();

        }
        catch
        {
            Console.WriteLine("Encryption failed!", "Error");
        }
    }

解密文件的代码

public  static void DecryptFile(string inputFile, string outputFile)
    {

        {
            string password = @"myKey123"; // Your Key Here

            UnicodeEncoding UE = new UnicodeEncoding();
           byte[] key = UE.GetBytes(password);



            FileStream fsCrypt = new FileStream(inputFile, FileMode.Open);

            RijndaelManaged RMCrypto = new RijndaelManaged();
            RMCrypto.Mode = CipherMode.CBC; //remember this parameter
            RMCrypto.Padding = PaddingMode.PKCS7;  //remember this parameter
            RMCrypto.KeySize = 0x80;
            RMCrypto.BlockSize = 0x80;
            CryptoStream cs = new CryptoStream(fsCrypt,
                RMCrypto.CreateDecryptor(key, key),
                CryptoStreamMode.Read);

            FileStream fsOut = new FileStream(outputFile, FileMode.Create);

            int data;
            while ((data = cs.ReadByte()) != -1)
                fsOut.WriteByte((byte)data);

            fsOut.Close();
            cs.Close();
            fsCrypt.Close();

        }
    }

我在 Android 中尝试使用以下代码

public static byte[] decodeFile(String key, byte[] fileData) throws Exception
{
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); //this parameters should not be changed
    byte[] keyBytes = new byte[16];
    byte[] b = key.getBytes("UTF-16");
    System.out.println("RAM"+b);
    int len = b.length;
    if (len > keyBytes.length)
        len = keyBytes.length;
    System.arraycopy(b, 0, keyBytes, 0, len);
    SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
    IvParameterSpec ivSpec = new IvParameterSpec(keyBytes);
    cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
    byte[] decrypted = cipher.doFinal(fileData);
    return decrypted;
}

运行此代码时出现错误:

error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt

【问题讨论】:

  • 跳出来的两件事:1)不应该任意截断key数组; 2) PKCS7 是 .NET 代码中使用的填充方案,而 PKCS5 是 android 代码中的填充方案。
  • 我也注意到了填充。 PKCS5 是 PKCS7 的子集,因此无法保证您的解密代码能够破译它(有时可能确实有效,但可能被认为是侥幸)。
  • 可以做些什么来解决这个问题。任何可以提供帮助的代码或库
  • 在 Java 中,使用 PKCS5 会导致用于填充的密码的块大小,在本例中为 16 字节,所以这很好。但是,如果您使用相同的密钥加密多个明文,您应该使用不同的 IV。另外,我建议您检查您的要求/您真正想做的事情。如果传输加密文件,密钥如何传输等?

标签: c# android algorithm encryption


【解决方案1】:

所以我发现您的代码和您使用的架构存在问题。 当您选择在不同语言程序和不同环境(Android(“unix”)和 Windows)中加密文件时,您需要记住小端和大端的概念。 - the wikipedia Endianness 。

在 Java 中,它总是使用 BIG endian 来威胁,因此最高有效字节 (MSB) 与使用最低有效字节 (LSB) 的 C# 不同,这一步会导致难以追踪的问题。

我根据您的代码创建了一个代码,但使用的是 Java 而不是 android,并且无法使代码工作,因为我总是有相同的错误消息 BadPaddingException: Given final block not properly padded。错误消息并没有说明key 文件的真正问题。
当您在 Java 中阅读时,它与 .NET 不同,因为当您将密钥转换为字节时,Java 架构威胁与 MSB,而您的真正密钥使用 LSB。

所以真正的答案是这个you need to convert your key to array of bytes telling to use the Least significative byte so you always have the same array of bytes in .NET and in Java
像这样的东西:

//the bKey it's the array of bytes and the key it's the String your are using.
byte[] bKey = key.getBytes("UTF-16LE");



I found the issue with the LE because i read the array of bytes from .NET and from Java and they are different so this gave me the start to find this issue

祝你的系统好运!

Ps.:我在解码时遇到了问题,因为您在解码和解码字节数组时遇到了问题。我找到了一条路径,您应该使用 Apache Commons Base64 来解码 Java 中的字符串。
参考

-大/小端 - Difference between Big Endian and little Endian Byte order
- 填充错误 - BadPaddingException: Given final block not properly padded
-提示问题出在关键 - Given final block not properly padded
-解码 Base64 - Decoding a Base64 string in Java




我用来测试的代码。

    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.nio.ByteBuffer;
    import java.nio.ByteOrder;
    import java.nio.charset.Charset;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import javax.crypto.Cipher;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;

    public class HelloWorld {

        public static void main(String[] args) throws Exception {
            // TODO Auto-generated method stub
            Path p = Paths
                    .get("C:\\Users\\casilva\\workspace\\StackOverflow\\src\\tst.enc");

            byte[] a = Files.readAllBytes(p);
            byte[] result = decodeFile("myKey123", a);
            System.out.println("Result=" + new String(result, "UTF-8"));
        }


        public static byte[] decodeFile(String key, byte[] fileData)
                throws Exception {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            byte[] bKey = key.getBytes("UTF-16LE");

            SecretKeySpec keySpec = new SecretKeySpec(bKey, "AES");

            IvParameterSpec ivSpec = new IvParameterSpec(bKey);

            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);

            byte[] decrypted = cipher.doFinal(fileData);

            return decrypted;
        }

    }

【讨论】:

  • 为寻求此类问题的人们提供完美的解决方案。我正在寻找这个解决方案一个多星期了。终于我得到了正确答案
猜你喜欢
  • 1970-01-01
  • 2013-03-01
  • 2011-05-15
  • 1970-01-01
  • 2018-04-08
  • 1970-01-01
  • 1970-01-01
  • 2013-06-22
相关资源
最近更新 更多