Android和C#之间的加密兼容性问题

21

我找到了很多有关如何在C#中进行加密的示例,也找到了一些Android示例,但我特别想找一种在Android中处理加密(使用AES、TripleDES等)并最终在C#中解密的方法。我在Android中找到了一个编码AES的示例,在C#中发现了编码/解码AES的代码,但不确定它们是否兼容(C#需要一个IV,在Android示例中没有指定)。此外,推荐一种用于通过HTTP传输加密字符串的编码方式(Base64?)会很有帮助。谢谢。


链接已失效。您能否添加您用于Android的代码片段? - Reaz Murshed
5个回答

42

我从http://oogifu.blogspot.com/2009/01/aes-in-java-and-c.html获得了一些帮助。

这是我的Java类:

package com.neocodenetworks.smsfwd;

import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import android.util.Log;

public class Crypto {
    public static final String TAG = "smsfwd";

    private static Cipher aesCipher;
    private static SecretKey secretKey;
    private static IvParameterSpec ivParameterSpec;

    private static String CIPHER_TRANSFORMATION = "AES/CBC/PKCS5Padding";
    private static String CIPHER_ALGORITHM = "AES";
    // Replace me with a 16-byte key, share between Java and C#
    private static byte[] rawSecretKey = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

    private static String MESSAGEDIGEST_ALGORITHM = "MD5";

    public Crypto(String passphrase) {
        byte[] passwordKey = encodeDigest(passphrase);

        try {
            aesCipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
        } catch (NoSuchAlgorithmException e) {
            Log.e(TAG, "No such algorithm " + CIPHER_ALGORITHM, e);
        } catch (NoSuchPaddingException e) {
            Log.e(TAG, "No such padding PKCS5", e);
        }

        secretKey = new SecretKeySpec(passwordKey, CIPHER_ALGORITHM);
        ivParameterSpec = new IvParameterSpec(rawSecretKey);
    }

    public String encryptAsBase64(byte[] clearData) {
        byte[] encryptedData = encrypt(clearData);
        return net.iharder.base64.Base64.encodeBytes(encryptedData);
    }

    public byte[] encrypt(byte[] clearData) {
        try {
            aesCipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
        } catch (InvalidKeyException e) {
            Log.e(TAG, "Invalid key", e);
            return null;
        } catch (InvalidAlgorithmParameterException e) {
            Log.e(TAG, "Invalid algorithm " + CIPHER_ALGORITHM, e);
            return null;
        }

        byte[] encryptedData;
        try {
            encryptedData = aesCipher.doFinal(clearData);
        } catch (IllegalBlockSizeException e) {
            Log.e(TAG, "Illegal block size", e);
            return null;
        } catch (BadPaddingException e) {
            Log.e(TAG, "Bad padding", e);
            return null;
        }
        return encryptedData;
    }

    private byte[] encodeDigest(String text) {
        MessageDigest digest;
        try {
            digest = MessageDigest.getInstance(MESSAGEDIGEST_ALGORITHM);
            return digest.digest(text.getBytes());
        } catch (NoSuchAlgorithmException e) {
            Log.e(TAG, "No such algorithm " + MESSAGEDIGEST_ALGORITHM, e);
        }

        return null;
    }
}

我使用了http://iharder.sourceforge.net/current/java/base64/来进行base64编码。

以下是我的C#类:

using System;
using System.Text;
using System.Security.Cryptography;

namespace smsfwdClient
{
    public class Crypto
    {
        private ICryptoTransform rijndaelDecryptor;
        // Replace me with a 16-byte key, share between Java and C#
        private static byte[] rawSecretKey = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

        public Crypto(string passphrase)
        {
            byte[] passwordKey = encodeDigest(passphrase);
            RijndaelManaged rijndael = new RijndaelManaged();
            rijndaelDecryptor = rijndael.CreateDecryptor(passwordKey, rawSecretKey);
        }

        public string Decrypt(byte[] encryptedData)
        {
            byte[] newClearData = rijndaelDecryptor.TransformFinalBlock(encryptedData, 0, encryptedData.Length);
            return Encoding.ASCII.GetString(newClearData);
        }

        public string DecryptFromBase64(string encryptedBase64)
        {
            return Decrypt(Convert.FromBase64String(encryptedBase64));
        }

        private byte[] encodeDigest(string text)
        {
            MD5CryptoServiceProvider x = new System.Security.Cryptography.MD5CryptoServiceProvider();
            byte[] data = Encoding.ASCII.GetBytes(text);
            return x.ComputeHash(data);
        }
    }
}

我真心希望这能帮助到其他人!


7
是的...我喜欢当人们用“哦,你应该这样做”或“哦,你应该那样做”来回答我的技术问题。感谢您的务实回答并留下了您的课程实现。 - nikib3ro
我已经使用你的Java代码加密了文件内容,并将加密后的内容保存到encrypted.txt中。现在我想使用openssl工具解密它,使用以下命令:openssl enc -d -a -p -aes-256-cbc -salt -in encrypted.txt -out decrypted.txt -pass pass:myPassword,但是我收到错误信息:bad magic number。有任何想法出了什么问题吗?即使我从命令中删除-salt参数,我仍然会收到相同的错误。 - uerceg
1
你为什么没有使用Base64.encodeToString(encryptedData, Base64.DEFAULT)而是使用了net.iharder.base64.Base64.encodeBytes(encryptedData)? - user1105748
2
Base64是在API级别8中引入的,该级别是在此答案发布三个月后发布的。 - Jess
我们如何使用这种方法加密数据? - Sirwan Afifi
显示剩余2条评论

4
在提供的c#源代码示例中,请注意这行代码:
Encoding.ASCII.GetString(newClearData);

UTF-8是Android的默认编码方式,因此加密字符串(尤其是非ASCII字符,例如中文)将作为UTF-8传递到C#。如果使用ASCII编码解码回字符串,文本将变得混乱不堪。下面是更好的方法:

Encoding.UTF8.GetString(newClearData);

谢谢!


1

没问题,只要密钥长度相同-128位AES和正确的块密码模式(CBC)。 你可能会遇到填充问题,但这应该很容易解决。 我最近在Java和Python中遇到了这些问题,但最终一切都解决了。 在HTTP上使用Base64进行编码应该没问题。 祝好运!


1

如果你在两端正确实现了相同的密码(如AES)和模式(如CTR,CFB,CCM等),则来自一端的密文可以被另一端解密,而不考虑平台。

你链接的Android示例似乎使用了ECB模式,因此对于你的目的来说并不安全。选择块模式的影响非常重要,你必须理解这一点。在这个层面上很容易出现加密错误,导致系统的安全性不如你想象的那么高。

编辑:我撤回之前的说法,它没有使用ECB,但它生成IV的方式并不实用。无论如何,我的观点是要理解块模式的影响。

你可以从wikipedia文章开始学习。Bruce Schneier的书“Practical Cryptography”对于任何实施加密安全的人都非常有价值。

至于编码字符串,如果你必须将字符串转换为ASCII文本,则Base64是一个不错的选择,但我建议你调查使用HTTP PUT或POST来避免这一额外步骤。


1
大多数互联网上的示例都是AES的弱实现。要使实现强大,应始终使用随机IV并对密钥进行散列。
要了解更安全(随机IV + 散列密钥)的跨平台(android、ios、c#)AES实现,请参见我的答案-https://dev59.com/UXTYa4cB1Zd3GeqPsDmq#24561148

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接