手机的短信加解密(一)
转载http://www.ophonesdn.com/article/show/133
标签 : 短信 加解密 DES 安全 密码学
前言
随着手机功能的丰富及手机与日常生活的紧密关系,手机信息安全问题越来越成为人们关注的焦点。手机内部信息的泄露、被窃取、遗失或被盗,都会造成重大安全隐患。随着3G时代的来临,手机的高普及率使得手机与个人的生活隐私、商业活动紧密相关,手机的使用安全和隐私安全问题已经突显出来。手机作为一个日益重要的信息载体,如何对其内部及通讯过程中的敏感信息进行有效保护,已经成为亟待解决的问题。
一、密码学概述【1】
使消息保密的技术和科学叫做密码编码学。
1.1 密码学基础
在密码学中,明文用M(消息)或P(明文)表示,它可能是比特流(文本文件、位图、数字化的语音流或数字化的视频图像)。至于涉及到计算机,P是简单地二进制数据(除了这一章节外,这本书本身只涉及二进制数据和计算机密码学)。明文可被传送或存储,无论在哪种情况,M指待加密的消息。密文用C表示,它也是二进制数据,有时和M一样大,有时稍大(通过压缩和加密的结合,C有可能比P小些。然而,单单加密通常达不到这一点)。加密函数E作用于M得到密文C,用数学表示为:
E(M)=C.
相反地,解密函数D作用于C产生M
D(C)=M.
先加密后再解密消息,原始的明文将恢复出来,下面的等式必须成立:
D(E(M))=M
1.2 密码学功能
除了提供机密性外,密码学通常有其它的作用:.
-鉴别:消息的接收者应该能够确认消息的来源;入侵者不可能伪装成他人。
-完整性:消息的接收者应该能够验证在传送过程中消息没有被修改;入侵者不可能用假消息代替合法消息。
-抗抵赖:发送者事后不可能虚假地否认他发送的消息。
1.3 算法和密钥
如果算法的保密性是基于保持算法的秘密,这种算法称为受限制的算法。受限制的算法具有历史意义,但按现在的标准,它们的保密性已远远不够。大的或经常变换的用户组织不能使用它们,因为每有一个用户离开这个组织,其它的用户就必须改换另外不同的算法。如果有人无意暴露了这个秘密,所有人都必须改变他们的算法。
现代密码学用密钥解决了这个问题,密钥用K表示。K可以是很多数值里的任意值。密钥K的可能值的范围叫做密钥空间。加密和解密运算都使用这个密钥(即运算都依赖于密钥,并用K作为下标表示),这样,加/解密函数现在变成:
EK(M)=C
DK(C)=M.
这些函数具有下面的特性:
DK(EK(M))=M.
有些算法使用不同的加密密钥和解密密钥,也就是说加密密钥K1与相应的解密密钥K2不同,在这种情况下:
EK1(M)=C
DK2(C)=M
DK2 (EK1(M))=M
所有这些算法的安全性都基于密钥的安全性;而不是基于算法的细节的安全性。这就意味着算法可以公开,也可以被分析,可以大量生产使用算法的产品,即使偷听者知道你的算法也没有关系;如果他不知道你使用的具体密钥,他就不可能阅读你的消息。
1.4 简单异或
异或在C语言中是“^”操作,或者用数学表达式⊕表示。它是对比特的标准操作:
0⊕0 = 0
0⊕1 = 1
1⊕0 = 1
1⊕1 = 0
参照《OPhone手机基础信息与基础业务》(http://www.ophonesdn.com/article/show/102),中的短信发送功能。我们选择0x55(0b01010101)作为密钥,和每一个字节做异或运算,实现加解密。这个算法可以用下列代码来实现,
- for(int i=0;i<msg.length();i++) encrymsg[i]=(char) (msg.charAt(i)^'U'); //U的ASCII码为0x55 msg=String.valueOf(encrymsg); sms.sendTextMessage(number, null, msg, pi, null);
for(int i=0;i<msg.length();i++) encrymsg[i]=(char) (msg.charAt(i)^'U'); //U的ASCII码为0x55 msg=String.valueOf(encrymsg); sms.sendTextMessage(number, null, msg, pi, null);
在发送端输入明文“helloworld”,短信经过加密如图1所示,变成了不可读的乱码。当我们将密文和0x55再次异或之后,可以获得原始的明文。这就实现了异或加解密。
1.5 不安全的异或
这种方法没有实际的保密性,这类加密可以通过下列两部被轻松破译。(1)用重合码计数法找出密钥长度。(2)按那个长度移动密文,并且和自己异或:这样就消除了密钥,留下明文和移动了密钥长度的明文的异或。
1.6 分组密码介绍
由于古典密码学(异或属于其中一种)在现在的计算机面前显得不堪一击,现代密码学开始发展。现代密码学中有对称密码、消息摘要函数、公钥密码(用于数字签名)等领域。本文通过循序渐进的办法,介绍加解密在手机信息安全中的应用,首先介绍的是分组密码。
分组密码取用明文的一个区块和钥匙,输出相同大小的密文区块。由于信息通常比单一区块还长,因此有了各种方式将连续的区块编织在一起。 DES和AES是美国联邦政府核定的分组密码标准,本文主要介绍DES算法。【2】
DES对64(bit)位的明文分组M进行操作,M经过一个初始置换IP置换成m0,将m0明文分成左半部分和右半部分m0=(L0,R0),各32位长。然后进行16轮完全相同的运算,这些运算被称为函数f,在运算过程中数据与密匙结合。经过16轮后,左,右半部分合在一起经过一个末置换,完成了加密。
在每一轮中,密匙位移位,然后再从密匙的56位中选出48位。通过一个扩展置换将数据的右半部分扩展成48位,并通过一个异或操作替代成新的32位数据,在将其置换换一次。这四步运算构成了函数f。然后,通过另一个异或运算,函数f的输出与左半部分结合,其结果成为新的右半部分,原来的右半部分成为新的左半部分。将该操作重复16次。流程如图2所示:
具体的算法可以参见官方文档:http://csrc.nist.gov/publications/fips/fips46-3/fips46-3.pdf
二、手机安全类介绍
OPhone手机平台上集成了若干信息安全的class,可以用来开发相关的软件。如图3所示。
本例采用DES算法,对通讯双方的短信进行加解密,其他人如果没有密钥,将不能获知信息内容。在本例中,用到了如下函数库:
- import java.security.spec.KeySpec;
- /*提供了加密/签名接口
- */
- import javax.crypto.Cipher;
- /*Cipher为加密和解密提供密码功能。它构成了 Java Cryptographic Extension (JCE) 框架的核心。
- 主要方法有:init:用密钥初始化此 Cipher
- doFinal:按单部分操作加密或解密数据,或者结束一个多部分操作。数据将被加密或解密(具体取决于此 Cipher 的初始化方式)。
- */
- import javax.crypto.SecretKey; /密钥
- import javax.crypto.SecretKeyFactory; //密钥工厂
- /*密钥工厂用来将密钥(类型 Key 的不透明加密密钥)转换为密钥规范(底层密钥材料的透明表示形式)。秘密密钥工厂只对秘密(对称)密钥进行操作。密钥工厂为双工模式,即其允许根据给定密钥规范(密钥材料)构建不透明密钥对象,或以适当格式获取密钥对象的底层密钥材料。密钥工厂的参数包括算法 ,加密模式,填充,密码协议
- */
- import javax.crypto.spec.DESKeySpec;
- /*实现java.security.spec.KeySpec接口,创建一个 DESKeySpec 对象。
- */
import java.security.spec.KeySpec; /*提供了加密/签名接口 */ import javax.crypto.Cipher; /*Cipher为加密和解密提供密码功能。它构成了 Java Cryptographic Extension (JCE) 框架的核心。 主要方法有:init:用密钥初始化此 Cipher doFinal:按单部分操作加密或解密数据,或者结束一个多部分操作。数据将被加密或解密(具体取决于此 Cipher 的初始化方式)。 */ import javax.crypto.SecretKey; /密钥 import javax.crypto.SecretKeyFactory; //密钥工厂 /*密钥工厂用来将密钥(类型 Key 的不透明加密密钥)转换为密钥规范(底层密钥材料的透明表示形式)。秘密密钥工厂只对秘密(对称)密钥进行操作。密钥工厂为双工模式,即其允许根据给定密钥规范(密钥材料)构建不透明密钥对象,或以适当格式获取密钥对象的底层密钥材料。密钥工厂的参数包括算法 ,加密模式,填充,密码协议 */ import javax.crypto.spec.DESKeySpec; /*实现java.security.spec.KeySpec接口,创建一个 DESKeySpec 对象。 */
三、实例编写
3.1 xml文件
为了实现短信发送功能,需要在AndroidManifest.xml中添加permission:
- <uses-permission android:name="android.permission.SEND_SMS"></uses-permission>
<uses-permission android:name="android.permission.SEND_SMS"></uses-permission>
为了实现短信接收功能,除了permission之外,还需要添加receiver:
- <receiver android:name="DecryptoSMS" android:enabled="true">
- <intent-filter>
- <action android:name="android.provider.Telephony.SMS_RECEIVED" />
- </intent-filter>
- </receiver>
- <uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>
<receiver android:name="DecryptoSMS" android:enabled="true"> <intent-filter> <action android:name="android.provider.Telephony.SMS_RECEIVED" /> </intent-filter> </receiver> <uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>
软件界面如图4所示:
运行后界面如图所示。左上角的输入框中填写接收端号码,右上角的输入框中填写密钥。由于DES的分块性质,密钥必须为8字节。SEND按钮提供无加密的短信发送方式。
3.2短信生成
ENCRYPT SEND按钮提供加密短信的发送功能,该按钮关键代码如下:
- byte des_key[]= enc_key.getBytes();/*从输入框获取密钥*/
- byte data[] = new byte[64]; /*初始化短信数据*/
- byte tempdata[]= et_text.getText().toString().getBytes();
- /*从输入框获取短信明文*/
- for (int i=0;i<tempdata.length;i++)
- data[i]=tempdata[i];
- if (tempdata.length <64){ /*复制短信内容至data,不足64位补0*/
- for (int i=data.length;i<64;i++)
- data[i]='\0';
- }
- byte result[] =des_crypt(des_key, data); /*进行加密*/
byte des_key[]= enc_key.getBytes();/*从输入框获取密钥*/ byte data[] = new byte[64]; /*初始化短信数据*/ byte tempdata[]= et_text.getText().toString().getBytes(); /*从输入框获取短信明文*/ for (int i=0;i<tempdata.length;i++) data[i]=tempdata[i]; if (tempdata.length <64){ /*复制短信内容至data,不足64位补0*/ for (int i=data.length;i<64;i++) data[i]='\0'; } byte result[] =des_crypt(des_key, data); /*进行加密*/
3.3 加密算法
des_crypt(des_key, data)方法是利用加密密钥和明文数据运算,获得密文的过程。代码如下:
- static String DES = "DES/ECB/NoPadding";
- /*设置加密方式,包括算法、模式、填充的参数,具体含义参见:http://java.sun.com/j2se/1.4.2/docs/guide/security/jce/JCERefGuide.html#AppA*/
- public static byte[] des_crypt(byte key[], byte data[]) {
- try {
- KeySpec ks = new DESKeySpec(key); /*新建密钥规范*/
- SecretKeyFactory kf = SecretKeyFactory.getInstance("DES");
- /* 设置密钥工厂模式为DES,可以设置为AES、DES、DESede、PBEWith等模式
- http://java.sun.com/j2se/1.4.2/docs/guide/security/jce/JCERefGuide.html#AppA
- */
- SecretKey ky = kf.generateSecret(ks); /*生成密钥*/
- Cipher c = Cipher.getInstance(DES); /*设置加密功能*/
- c.init(Cipher.ENCRYPT_MODE, ky); /*加密初始化*/
- return c.doFinal(data); /*加密运算*/
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
static String DES = "DES/ECB/NoPadding"; /*设置加密方式,包括算法、模式、填充的参数,具体含义参见:http://java.sun.com/j2se/1.4.2/docs/guide/security/jce/JCERefGuide.html#AppA*/ public static byte[] des_crypt(byte key[], byte data[]) { try { KeySpec ks = new DESKeySpec(key); /*新建密钥规范*/ SecretKeyFactory kf = SecretKeyFactory.getInstance("DES"); /* 设置密钥工厂模式为DES,可以设置为AES、DES、DESede、PBEWith等模式 http://java.sun.com/j2se/1.4.2/docs/guide/security/jce/JCERefGuide.html#AppA */ SecretKey ky = kf.generateSecret(ks); /*生成密钥*/ Cipher c = Cipher.getInstance(DES); /*设置加密功能*/ c.init(Cipher.ENCRYPT_MODE, ky); /*加密初始化*/ return c.doFinal(data); /*加密运算*/ } catch (Exception e) { e.printStackTrace(); return null; } }
3.4 数据短信发送
获得加密结果后,需要将数据发送出去。在之前的短信发送软件中,用的是sendTextMessage方法,该方法只能发送文本数据。短信经过加密之后,会产生手机不能显示的非文本字符,所以我们需要用sendDataMessage方法来发送加密数据:
- public final void sendDataMessage (String destinationAddress, String scAddress, short destinationPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent)。
public final void sendDataMessage (String destinationAddress, String scAddress, short destinationPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent)。
和sendTextMessage不同之处在于发送数据类型变成了byte,并且增加了目标端口号参数。
- SmsManager sms = SmsManager.getDefault();/*建立短信发送管理对象*/
- try {
- short pt=80; /*设置端口号*/
- PendingIntent pi = PendingIntent.getBroadcast(CryptoSMS.this, 0, new Intent(), 0);
- sms.sendDataMessage(et_num.getText().toString(), null,pt, result, pi, null); /*填写接收号码、短信数据等参数,并发送短信*/
- Toast.makeText(CryptoSMS.this,"发送成功",Toast.LENGTH_LONG).show ();
- } catch (Exception e) {
- Toast.makeText(CryptoSMS.this, "发送失败\n"+e.getMessage(),Toast. LENGTH_LONG).show();
- }
SmsManager sms = SmsManager.getDefault();/*建立短信发送管理对象*/ try { short pt=80; /*设置端口号*/ PendingIntent pi = PendingIntent.getBroadcast(CryptoSMS.this, 0, new Intent(), 0); sms.sendDataMessage(et_num.getText().toString(), null,pt, result, pi, null); /*填写接收号码、短信数据等参数,并发送短信*/ Toast.makeText(CryptoSMS.this,"发送成功",Toast.LENGTH_LONG).show (); } catch (Exception e) { Toast.makeText(CryptoSMS.this, "发送失败\n"+e.getMessage(),Toast. LENGTH_LONG).show(); }
3.5 短信接收
接收短信采用BroadcastReceiver实现,需要覆盖onReceive方法。在接收到消息后,取出其中的pdu(Protocol Data Unit,协议数据单元)进行解密,获得短信明文。
- public class DecryptoSMS extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- Bundle bundle = intent.getExtras();
- Object pdus[] = (Object[]) bundle.get("pdus");/*获得pdu*/
- byte[] tempdata = SmsMessage.createFromPdu((byte[]) pdus[0]).getUserData();/*转换为byte格式*/
- String enc_key= "KEYabcde";
- byte des_key[]= enc_key.getBytes();
- int datalength = (tempdata.length >> 3)*8;
- /*调整密文数据为64bit的倍数*/
- byte data[] = new byte[datalength];
- for (int i=0;i<datalength;i++)
- if (i<64) data[i]=tempdata[i];
- /*为简便起见,设置短信长度不超过64字节*/
- byte result[] = des_decrypt(des_key,data);
- String value = new String(result); /*转换为字符串*/
- Toast.makeText(context,"Received SMS:"+value, Toast.LENGTH_LONG).show();/*提示收到明文短信*/
- }
public class DecryptoSMS extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Bundle bundle = intent.getExtras(); Object pdus[] = (Object[]) bundle.get("pdus");/*获得pdu*/ byte[] tempdata = SmsMessage.createFromPdu((byte[]) pdus[0]).getUserData();/*转换为byte格式*/ String enc_key= "KEYabcde"; byte des_key[]= enc_key.getBytes(); int datalength = (tempdata.length >> 3)*8; /*调整密文数据为64bit的倍数*/ byte data[] = new byte[datalength]; for (int i=0;i<datalength;i++) if (i<64) data[i]=tempdata[i]; /*为简便起见,设置短信长度不超过64字节*/ byte result[] = des_decrypt(des_key,data); String value = new String(result); /*转换为字符串*/ Toast.makeText(context,"Received SMS:"+value, Toast.LENGTH_LONG).show();/*提示收到明文短信*/ }
3.6 短信解密
解密代码仅仅在cipher的初始化参数有不同,其他完全一样:
- public static byte[] des_decrypt(byte key[], byte data[]) {
- try {
- KeySpec ks = new DESKeySpec(key);
- SecretKeyFactory kf = SecretKeyFactory.getInstance("DES");
- SecretKey ky = kf.generateSecret(ks);
- Cipher c = Cipher.getInstance(DES);
- c.init(Cipher.DECRYPT_MODE, ky);
- return c.doFinal(data);
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
public static byte[] des_decrypt(byte key[], byte data[]) { try { KeySpec ks = new DESKeySpec(key); SecretKeyFactory kf = SecretKeyFactory.getInstance("DES"); SecretKey ky = kf.generateSecret(ks); Cipher c = Cipher.getInstance(DES); c.init(Cipher.DECRYPT_MODE, ky); return c.doFinal(data); } catch (Exception e) { e.printStackTrace(); return null; } }
四、运行测试
4.1 运行环境
为了在模拟器上显示短信的收发过程,必须运行两个模拟器。在OMS1.0的平台上,只需在eclipse中run两次就可以了;在OMS1.5平台,启动模拟器必须先创建一个 AVD( Android Virtual Device 虚拟设备 ) 。通过下列命令创建:
android create avd --target 1 --name myavd
在工程的Run configuration中(如图5所示)配置该工程选用哪个模拟器,在图6中,可以在指定运行的模拟器AVD上打勾。
4.2 运行结果
运行软件后,在输入框填写号码、密钥、短信明文,点击ENCRYPT SEND,稍后在另外一个模拟器就能收到密文和明文,如图7所示。可以看到,在收件箱中收到的是乱码,但是在Toast中显示的是解密的结果。
五、总结
本文介绍了用DES算法加密短信的办法,简单实用。
DES由于其算法简单,可以在几天内被攻破,现在正在逐渐淡出加密舞台。但是,3重DES(triple-DES)还是具有很强的保密性和实用性,其官方文档链接地址如下:http://webstore.ansi.org/RecordDetail.aspx?sku=ANSI+X9.52%3a1998 用户可以通过在DES的基础上略微修改实现。另外,AES(Advanced Encryption Standard)算法的安全性大大超过了DES,当前具有很广的应用。在Cipher的参数中可以选择AES算法来提高安全性,
但是和三重DES一样,用户需要牺牲运算时间。值得注意的是AES的明文分块是128bit的,密钥是128/192/256bit的,需要在代码中修改byte数组的长度来实现。
参考文献
【1】《应用密码学--协议、算法与C源程序》,译者: 吴世忠等,作者: (美)旋奈尔(Schneier,B.)
【2】对称算法(一)DES算法,http://www.infosecurity.org.cn/article/pki/cryptography/23407.html
作者介绍
周轩,就职于浙江移动,OPhone SDN入门版版主,北邮通信硕士,擅长嵌入式系统底层开发,关注移动互联网的发展。
(声明:本网的新闻及文章版权均属OPhone SDN网站所有,如需转载请与我们编辑团队联系。任何媒体、网站或个人未经本网书面协议授权,不得进行任何形式的转载。已经取得本网协议授权的媒体、网站,在转载使用时请注明稿件来源。)