来源: https://github.com/Pavel-Durov/CodeProject-Android-and-NET-Encryption
源码下载:
前言
可能被非法用户看到的敏感数据应该被加密。
哪些是敏感数据由你决定,可以是个人资料、在网路中传输的图片,你的地理位置等等。
敏感数据是易于泄漏的,当它在程序中被暴漏时,在网络中传输时,或者存储在本地文件时。
这篇文章不是关于安全的,仅仅介绍简单的加解密实现。
什么是加密?
加密是将一段明文转换成密文。
解密是相反的,是将密文转换成可读的明文。
加密算法可以被分为两种,对称和非对称。两者都有优点和缺点。
对称加密
用一个相同的共享密钥执行加密和解密。密钥在发送和接受者之间共享。
为了对消息进行加密,发送者使用共享密钥;接收方必须使用相同的密钥来解密消息。
对称算法非常快,但它需要双方有一个特定的、相同的密钥,这在大系统中会是一个问题,因为涉及到大量的密钥管理。
非对称加密(公钥密码体制)
消息发送者和接受者使用不同的密钥对消息进行加密和解密,每一方都有一对密钥,公钥和私钥。
当发送者要加密消息时,他使用发送者公钥。接受者可以使用他的私钥对消息进行解密。这种方法相对较慢,但在大型系统中,密钥管理更容易。
安卓中的对称加密
在这个例子中我们将使用Android的SDK中的类cipher来实现我们加密/解密的目的。
Cipher
类提供了进行加密和解密的密钥的实现。查看更多信息:
http://developer.android.com/reference/javax/crypto/Cipher.html
CryptoHandler 类
Cipher
取决于两个键,第一个是作为字符串传递给构造函数的密钥,第二个是原始密钥,它是16字节长的。
CryptoHandler
使用实例。
<a name="CryptoHandlerClass"> //CryptoHandler构造方法 public CryptoHandler(String passphrase) { //将加密信息转换为字节数组 byte[] passwordKey = encodeDigest(passphrase); try { //_aesCipher得到特定加密算法实例 _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 类的私有成员 _secretKey = new SecretKeySpec(passwordKey, CIPHER_ALGORITHM); //创建一个新的ivparameterspec实例,使用初始向量iv _ivParameterSpec = new IvParameterSpec(rawSecretKey); }</a>
在CryptoHandler
中我们使用AES (Advanced Encryption Standard) 算法加密,你可以在这里阅读更多信息:
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 – 加密算法 (Advanced Encryption Standard).
CBC – 反馈模式的名称(在我们的例子中是密码块链接)。
PKCS5Padding – 填充语法名称。
CryptoHandler
类的方法
除了构造函数,我们在CryptoHandler类中还有3个其他方法:
<a name="CryptoHandlerClassMethods"> public byte[] Encrypt(byte[] clearData)</a>
Encrypt
加密方法通过将Cipher.ENCRYPT_MODE常量传递给init()方法将_aesCipher设置为加密模式。
<a name="CryptoHandlerClassMethods"> public byte[] Decrypt(byte[] data)</a>
Decrypt
解密方法也是这样,但不是传递Cipher.ENCRYPT_MODE 而是传递Cipher.DECRYPT_MODE。
解密加密方法都调用DoWork()方法,它们之间的唯一区别是每个设置不同的Cipher
密码模式常数。
<a name="CryptoHandlerClassMethods"> public byte[] DoWork(byte[] data)</a>
调用_aesCipher.doFinal(数据)方法并捕获异常。
<a name="CryptoHandlerClassMethods"> private byte[] encodeDigest(String text)</a>
在构造函数中使用一次以进行密码短语编码。
私有原始密钥作为字节数组存储在类中:
<a name="CryptoHandlerClassMethods"> private static byte[] _rawSecretKey = { 0x12, 0x00, 0x22, 0x55, 0x33, 0x78, 0x25, 0x11, 0x33, 0x45, 0x00, 0x00, 0x34, 0x00, 0x23, 0x28 };</a>
密钥必须在发送者和接受者之间共享,在我们的例子中,我们同时作为消息发送和接收者,所以我们使用的是同一个密钥实例进行加密和解密。
Android 实现
在MainActivity
布局文件,你可以看到两个颜色的区域,蓝色的供你输入文本进行加密,绿色的用来显示加密后的串。
当你按下加密按钮,加密的数据将被保存到内部文件系统(你可以在文件夹中看到它),并且在绿色块中展示出来,如图中所示乱码(它是原明文的密文)。
*保存的数据将覆盖前面的,不会被追加到文件中的已有数据的后面。
<a name="AndroidImplementation"> /** * * 加密在_tvmessage EditText中输入的字符串值 * 并且保存在本地文件系统中 * * */ private void EncryptMessage() { String message = _tvMessage.getText().toString(); if(message != null && message.length() > 0) { //执行文本加密 byte[] bytes = _crypto.Encrypt(message.getBytes()); //设置视图的值 SetTextViewEncryptresult(bytes); //保存密文到文件系统中 _streamHandler.SaveTextFile ( FileStreamHandler.ENCRYPTED_TXT_FILENAME, bytes ); } }</a>
*如果你不知道如何查看你的安卓设备的内部文件,查看我的文章:
http://www.codeproject.com/Articles/825304/Accessing-internal-data-on-Android-device
所有文件操作是通过handlers包中的FileStreamHandler处理的。
下面有几个方法负责读和写一个安卓内部存储。
当你点击在MainActivity
布局中的“解密”按钮,信息会从保存的文件中读取出来,展示在你的Toast 提示中。
/** * 读取保存在本地文件的内容并解密 * 调用ShowToast()方法 * */ private void DecryptMessage() { byte[] fileContent = _streamHandler .ReadFromLocalFile(FileStreamHandler.ENCRYPTED_TXT_FILENAME); if(fileContent != null && fileContent.length > 0) { //执行文件内容解密 byte[] decrypted = _crypto.Decrypt(fileContent); //将传递的字节数组通过utf8编码创建新的字符串实例 String readableData = StringHandler.GetString(decrypted); String encrypted = StringHandler.GetString(fileContent); if(readableData != null && encrypted != null) { //显示提示 ShowToast ( getString(R.string.msg_decrypted) + readableData, getString(R.string.msg_encrypted) + encrypted ); } } else { //如果文件不存在或文件内容是空的 ShowToast(":(", "!"); } }
如果你点击了“解密”按钮,你将得到如下结果:
那意味着你完成整个加解密过程。
你输入一段文本,文本将会被加密成密文,并保存到本地文件,稍后被读取并解密为正常可读的文本。
在这个例子中我们使用java类硬编码的值简单的保存密钥和密码信息,但是这不是一个最佳的解决方案,因为它可以很容易地利用逆向工程进行查看,所以记住这件事,将钥匙放在安全的地方。
在.NET上解密Android消息
现在我们将看到如何使用C#语言在.NET上解密在Android设备上加密的密文。这也可以在其他平台上完成,因为我们使用标准加密。
我们将我们以前在Android设备上保存的文件拖到机器上,并将其导入Visual Studio上的基本控制台应用程序。
好了,让我们创建文件,并使用adb拖出文件:
步骤:
- 在蓝色区域中输入消息,然后按加密Encrypt 按钮。
现在我们的消息被加密并保存到硬盘上。
2. 接下来,我们将运行几个命令授予文件权限,并使用pull命令从设备获取文件。
*再次声明,如果你不明白,请看看我的文章--关于adb工具和Android内部文件的权限:
http://www.codeproject.com/Articles/825304/Accessing-internal-data-on-Android-device
由于我所有的adb命令都执行成功,我现在可以在"下载"目录中浏览我的文件:
现在我们要将它导入到我们的.NET控制台应用程序。
*不要复制文本,我们在这里处理二进制数据,把它作为字符串拷贝可能改变其值! 只需复制文件。
在我们的C#控制台应用程序中,我们得到了CryptoHandler类,它基本上与Android上的相同。 注意在.NET上,我们使用ICryptoTransform的两个实例作为解密和加密机制。 这几乎是一样的,在Android上我们使用常量,在这里我们使用RijndaelManaged.NET类的工厂方法。
如果每一件事情都按原样运行,运行C#程序,您将看到相同的消息,与在Android设备上输入得到的提示一样:
总结
我们加密和解密我们在Android和.NET应用程序上的简单消息。 这可以作为客户端 - 服务器通信的一部分(Android作为客户端,.NET作为服务器),使用您希望加密的文本或任何其他二进制文件,除非你将需要通过网络处理数据转换,这里例子没有包含这种情况。
不要运行和加密你手中的每一份数据,特别是当你处理大型二进制文件。 这可能会降低您的应用程序效率,尤其是如果您正在保护没有人感兴趣的数据。

- 原文:Android and .NET Encryption. / 谈谈 Android 与 .NET 的加密
- 作者:Pavel Durov
- 频道:计算机
- 发布:coyee (2016-10-09)
- 标签: Android.NET
- 版权:本文仅用于学习、研究和交流目的,非商业转载请注明出处、译者和可译网完整链接。
文章评论