Java和Objective-C中字符编码及DES加密解密
1.概述
在基于互联网的应用中,发送端将字符采用某种方式加密后传输;而接受端根据事先约定的密钥进行解密,这样即使传输的字符被截获,也不会轻易被识别。而且,现在很多应用环境都很复杂,服务端是JAVA应用,客户端有JAVA应用、智能手机应用。我们以服务端为JAVA应用,客户端为智能手机IOS应用为例,实现在服务端加密一段字符,传输到客户端解密;在客户端又加密一段字符,传输到服务端解密,这样一个较为复杂的过程。
对于这种需求,有很多实现方式,如采用https加安全数字证书来实现,它在金融行业用得比较多。
这里采用DES算法完成这种字符安全传输的需求。首先声明一下,DES算法我也了解不多,下面的论述肯定有遗漏、错误等等不足之处,请参考性阅读,发现错误等请告诉我。在此先行谢过。
2.字符编码
所有计算机的字符都是按照某种字符集进行编码的,在网络中真正传输的是字节。
在客户端发送某串字符如”miki西游:mikixiyou@126.com”,它会按照客户端的字符集进行编码,形成字节流,在传输到某个服务端前,还需要使用BASE64进行编码,然后传到服务端。
服务端接收到之后,使用BASE64进行解码,然后按照它的默认字符集进行解码,形成字符串。如果客户端和服务端使用的默认字符集是一致的,如都是GBK,那么就会正确显示这段字符文字。如果不正确,如客户端用GBK编码,而服务器端用UTF8编码,那么就会出现乱码。我们经常在浏览器上见到乱码啊问号号等字符,就是这样字符集不一致所导致的。
以Java代码为例,解释一下字符如何编码,如何转换的操作过程。
public static void main(String args[]) throws Exception {
Stringsource = "miki西游|mikixiyou@126.com";
StringcharsetName=System.getProperty("file.encoding");
System.out.println("file.encodingis "+charsetName);
System.out.println("source="+source);
System.out.println(parseByte2HexStr(source.getBytes("GBK")));
System.out.println(parseByte2HexStr(source.getBytes()));
System.out.println(parseByte2HexStr(source.getBytes("UTF-8")));
String source_utf8=newString (source.getBytes("UTF-8"),"UTF-8");
System.out.println("source_utf8="+source_utf8);
String source_gbk=newString (source.getBytes("GBK"),"GBK");
System.out.println("source_gbk="+source_gbk);
}
在这段代码中,我们使用这个方法source.getBytes()的source字符串默认字符集的编码。
String source = " miki西游| mikixiyou@126.com;
System.out.println(parseByte2HexStr(source.getBytes()));
输出结果为
6D696B69 CEF7 D3CE 7C206D696B697869796F75403132362E636F6D
前两个字节CEF7表示“西”,后两个字节D3CE表示“游”。GBK字符集对于汉字采用两字节编码的。
字符串source的默认字符集可以通过系统属性得到,它是每一个JAVA的文件编码。获取的方法如下:
StringcharsetName=System.getProperty("file.encoding");
System.out.println("file.encodingis "+charsetName);
输出结果为
file.encoding is GBK
如果按照UTF-8字符集获取编码,那么输出的字节流将按照UTF-8编码方式进行输出。
System.out.println(parseByte2HexStr(source.getBytes("UTF-8")));
String source_utf8=newString (source.getBytes("UTF-8"),"UTF-8");
输出结果为
6D696B69 E8A5BF E6B8B8 7C206D696B697869796F75403132362E636F6D
三个字节E8A5BF表示“西”,三个字节E6B8B8表示“游”。UTF-8字符集对于汉字采用三字节编码的,对于英文字符及数字还是单字节编码。
在互联网中,传输的字节流还需要进行BASE64编码。我不知道是不是因为字节流太长了什么的,需要BASE64编码压缩一下,还是其他什么目的。
BASE64的使用很简单,网上源代码很多。基本是使用这两个方法,“String encode(byte[] data)“将字节数组编码成字符串,“byte[]decode(String s)”将字符串还原成字节数组。
不管字符是采用UTF-8字符集,还是GBK字符集,以及其他的字符集。这些字符都是一串二进制数字,和对应的字符集的对应,从而形成人眼能理解的字符。
在Objective-C中,也是遵循这个规则的。
NSData* data=[plainTextdataUsingEncoding: NSUTF8StringEncoding];
NSLog(@"plainTextBytes with UTF-8 encoding:%@",[XYDESdataToHex:data]);
这是将字符串plainText以UTF8字符串编码方式生成一个字节流。
它的输出结果如下:
plainTextBytes with UTF-8 encoding:6D696B69 E8A5BF E6B8B8 7C206D696B697869796F75403132362E636F6D
三个字节E8A5BF表示“西”,三个字节E6B8B8表示“游”。UTF-8字符集对于汉字采用三字节编码,对于英文字符及数字还是单字节编码。
从这里可以看到字符编码都是UTF8时,不管是Java还是Objective-C语言中,得到的字节流都是一样的。
在这个字节流的基础上,使用Base64再次进行编码。我在Objective-C中采用的是google提供的GTMBase64进行编码和解码。
在JAVA类中导入 javax.crypto.Cipher;包,使用Cipher.getInstance("DES/CBC/PKCS5Padding");方法实现加密。
注意,这里使用PKCS5Padding算法,密钥只能是8个字节。
因为在ios中,支持的DES加密算法是kCCOptionPKCS7Padding |kCCOptionECBMode。在使用PKCS7Padding,它的密钥可以是8个字节,也可以不是。如果密钥不是8个字节的话,那么JAVA端的PKCS5Padding算法就不能解密了。
我对DES算法也了解甚少,这里只说一下自己的理解。在密钥都是8个字节的前提下,PKCS7Padding和PKCS5Padding的加密和解密是通用的。因此,不必纠结于两个算法不一样怎么办,如何让IOS也支持JAVA的加密算法,甚至不用DES了等等。
我觉得都没必要,我们做的是工程,一种需求的实现方法。只要遵守密钥为8个字节的约定,就能实现需求,又何必去找其他的算法。好吧,我理解你觉得这样不安全,其实也没绝对的安全。
回到正题,JAVA中DES加密实现方法如下:
private static byte[] iv = {1, 2, 3, 4, 5, 6, 7, 8};
public static byte[]encryptDES(String encryptString, String encryptKey)
throws Exception {
System.out.println("willencryptedData with UTF-8 encoding =" +parseByte2HexStr(encryptString.getBytes("UTF-8")));
IvParameterSpec zeroIv =new IvParameterSpec(iv);
SecretKeySpec key = newSecretKeySpec(encryptKey.getBytes(), "DES");
Cipher cipher =Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE,key, zeroIv);
byte[] encryptedData =cipher.doFinal(encryptString.getBytes("UTF-8"));
System.out.println("didencryptedData =" +parseByte2HexStr(encryptedData));
return encryptedData;
}
public static StringencryptDESwithBase64(String encryptString,String encryptKey) throws Exception
{
returnXYBase64.encode(encryptDES(encryptString,encryptKey));
}
JAVA中DES解密实现方法如下:
public static String decryptDES(byte[] encryptedData, String decryptKey)
throws Exception {
补充:软件开发 , Java ,