Android 和 .NET 加密。






4.93/5 (15投票s)
本文将简要介绍如何在 Android 和 .NET 平台上进行基本的加密/解密,重点在于将加密数据从 Android 传递到 .NET。
来源
Github: https://github.com/Pavel-Durov/CodeProject-Android-and-NET-Encryption
直接
目录
前言
应将可能被他人窥探到的敏感数据进行加密。
什么是敏感信息由您决定。它可以是任何个人数据、您在网络上传输的图片、您的 GPS 位置等……
当敏感数据暴露在程序外部、发送到互联网或保存到本地文件系统时,它就处于危险之中。
本文并非关于安全,而是关于简单的加密/解密实现。
什么是加密?
加密算法可分为两大类:对称加密和非对称加密。两者各有优缺点。
对称加密
加密和解密都使用同一个共享密钥。该密钥在发送方和接收方之间共享。
为了加密消息,发送方使用共享密钥;接收方也必须使用相同的密钥来解密消息。
对称算法速度很快,但要求双方拥有一个独一无二的相同密钥,这在大系统中可能成为一个问题,因为密钥管理会非常复杂。
非对称加密(公钥密码学)
发送方和接收方使用不同的密钥来加密和解密消息,每个人都拥有一对密钥——公钥和私钥。
当发送方要加密消息时,他会使用接收方的公钥。然后接收方可以使用自己的私钥解密该消息。这种方法速度较慢,但在大型系统中密钥管理要容易得多。
Android 中的对称加密
在本例中,我们将使用 Android 的 SDK 类 Cipher
来进行加密/解密。
Cipher
类提供了对加密和解密密码实现(ciphers)的访问。更多信息请参阅
https://developer.android.com.cn/reference/javax/crypto/Cipher.html
CryptoHandler 类
我们的 Cipher
依赖于两个密钥,第一个是通过字符串传递给构造函数的密码短语,第二个是 16 字节的原始密钥。
//CryptoHandler constructor
public CryptoHandler(String passphrase)
{
//decodes passd phrase to encrypted byte[]
byte[] passwordKey = encodeDigest(passphrase);
try
{
//_aesCipher instantiation passing transformation parameter
_aesCipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
}
catch (NoSuchAlgorithmException e)
{ //Invalid algorithm in passed transformation parameter
Log.e(TAG, "No such algorithm " + CIPHER_ALGORITHM, e);
}
catch (NoSuchPaddingException e)
{ //Invalid padding in passed transformation parameter
Log.e(TAG, "No such padding PKCS5", e);
}
//Encodes the passed password phrase to a byte[]
//that will be stored in class private member of SecretKey type
_secretKey = new SecretKeySpec(passwordKey, CIPHER_ALGORITHM);
//Creates a new IvParameterSpec instance with the bytes
//from the specified buffer iv used as initialization vector.
_ivParameterSpec = new IvParameterSpec(rawSecretKey);
}
在我们的 CryptoHandler
类中,我们将使用 AES(高级加密标准)加密,您可以在这里阅读更多相关信息:
http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
AES 的实例化发生在下面这行代码中:
private static String CIPHER_TRANSFORMATION = "AES/CBC/PKCS5Padding";
_aesCipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
我们调用了一个 Cipher
的静态方法,它返回一个 Cipher
类型的实例化对象(如果没有抛出异常)。
“AES/CBC/PKCS5Padding”(传递的字符串转换参数)
AES – 加密算法(高级加密标准)。
CBC – 一种反馈模式的名称(在我们的例子中是密码块链接)。
PKCS5Padding – 填充方案的名称。
CryptoHandler
类的方法
除了构造函数,我们的 CryptoHandler
类还有另外 3 个方法:
public byte[] Encrypt(byte[] clearData)
Encrypt
方法通过将 Cipher.ENCRYPT_MODE 常量传递给 init() 方法,将 _aesCipher 设置为加密模式。
public byte[] Decrypt(byte[] data)
Decrypt
方法做同样的事情,但传递的不是 Cipher.ENCRYPT_MODE,而是 Cipher.DECRYPT_MODE。
Decrypt
和 Encrypt
方法都调用 DoWork()
方法,它们之间唯一的区别在于各自设置的 Cipher
模式常量。
public byte[] DoWork(byte[] data)
调用 _aesCipher.doFinal(data)
方法并捕获异常。
private byte[] encodeDigest(String text)
private static byte[] _rawSecretKey =
{
0x12, 0x00, 0x22, 0x55, 0x33, 0x78, 0x25, 0x11,
0x33, 0x45, 0x00, 0x00, 0x34, 0x00, 0x23, 0x28
};
此密钥必须在发送方和接收方之间共享,在我们的例子中,我们既是发送方也是接收方,所以我们使用相同的密钥实例进行加密和解密。
Android 实现
在 MainActivity
的布局中,您可以看到两个着色的区域,蓝色区域是输入要加密文本的地方,绿色区域仅用于显示。
当您按下“加密”按钮时,加密后的数据将被保存在内部文件系统中(您可以在文件文件夹下看到它),并在绿色视图中显示为乱码(这是原始明文的密文)。
*保存的数据将覆盖之前的数据,而不会追加到文件中的现有数据。
/**
* Encrypts the String value that entered in the _tvMessage EditText View
* and saves is to file on local file system
* */
private void EncryptMessage()
{
String message = _tvMessage.getText().toString();
if(message != null && message.length() > 0)
{
//performs text encryption
byte[] bytes = _crypto.Encrypt(message.getBytes());
//sets view value
SetTextViewEncryptresult(bytes);
//saves encrypted text to internal file
_streamHandler.SaveTextFile
(
FileStreamHandler.ENCRYPTED_TXT_FILENAME,
bytes
);
}
}
*如果您不知道如何查看 Android 设备的内部数据,请参阅我的文章:
https://codeproject.org.cn/Articles/825304/Accessing-internal-data-on-Android-device
所有文件操作都通过 handlers 包中的 FileStreamHandler
执行。
有几个方法负责读取和写入内部文件。
当您点击 MainActivity
布局上的 Decrypt
按钮时,消息将从保存的文件中读取并以 Toast 消息的形式显示给您。
/**
* Decrypt the message from saved local file content.
* calls ShowToast() method
* */
private void DecryptMessage()
{
byte[] fileContent = _streamHandler
.ReadFromLocalFile(FileStreamHandler.ENCRYPTED_TXT_FILENAME);
if(fileContent != null && fileContent.length > 0)
{
//preforms decryption of the fuile content
byte[] decrypted = _crypto.Decrypt(fileContent);
//Creates new String instance of passed byte[] as UTF-8
String readableData = StringHandler.GetString(decrypted);
String encrypted = StringHandler.GetString(fileContent);
if(readableData != null && encrypted != null)
{
//showing toast
ShowToast
(
getString(R.string.msg_decrypted) + readableData,
getString(R.string.msg_encrypted) + encrypted
);
}
}
else
{ //if file not exist or file content is empty
ShowToast(":(", "!");
}
}
如果您点击了“解密”按钮并得到了类似这样的结果:
这意味着您已完成整个加密周期。
您输入了明文,将其编码为密文并保存到本地文件,然后读取该文件并将其解密为正常的、可读的文本。
在本例中,我们将密钥和密码短语作为硬编码值保存在 java 类中,但这并非最佳解决方案,因为它很容易通过逆向工程进行检查,因此请牢记这一点,并将密钥存储在安全的地方。
在 .NET 中解密 Android 消息
现在,我们将看到如何使用 C# 语言在 .NET 中解密在 Android 设备上加密的密文。由于我们使用的是标准加密,因此也可以在其他平台上进行。
我们将把之前在 Android 设备上保存的文件拉取到计算机上,并将其导入到 Visual Studio 中的基本控制台应用程序。
2. 接下来,我们将运行几个命令来授予文件权限并使用 pull 命令从设备获取文件。
*再次说明,如果您不理解,请参阅我关于 adb 工具和 Android 内部文件权限的文章。
https://codeproject.org.cn/Articles/825304/Accessing-internal-data-on-Android-device
由于我所有的 adb 命令都已成功执行,我现在可以在我的下载目录中浏览我的文件了。
现在我们将它导入到我们的 .NET 控制台应用程序中。
*请勿复制文本,我们处理的是二进制数据,将其作为字符串复制可能会改变其值!只需按原样复制文件。
在我们的 C# 控制台应用程序中,我们有一个 CryptoHandler 类,它基本上与 Android 上的相同。请注意,在 .NET 中,我们使用了两个 ICryptoTransform
实例作为解密和加密机制。这大致相同,因为在 Android 中我们使用了常量,而在 .NET 中我们使用了 RijndaelManaged
.NET 类的工厂方法。
如果一切顺利,运行 C# 程序,您将看到与在 Android 设备上输入的相同消息。
摘要
我们已经成功地在 Android 和 .NET 应用程序中加密和解密了我们的简单消息。这可以作为客户端-服务器通信(Android 作为客户端,.NET 作为服务器)的一部分,用于处理文本或任何您想加密的其他二进制文件,只需处理网络上的数据传输即可,这部分我们并未涵盖。
不要随意运行和加密您手头的所有东西,尤其是在处理大型二进制文件时。这会降低您的应用程序效率,特别是当您要保护的数据实际上没有人感兴趣时。