• (好文)关于 Objectivec 和 Java 下 DES 加密保持一致的方式


    参考自:http://www.helmsmansoft.com/index.php/archives/1179

    PS By Yang3wei: 

    做了一些修改,原文缺失的一些东西真是让人很蛋疼,从下午一直弄到晚上11点半,哎~

    谈一下需要注意的一些地方:

    1。CommonCrypto是苹果的sdk库,引入了 <CommonCrypto/CommonCryptor.h> 便可以使用到诸多加解密算法。

    我开始还以为 CommonCrypto 是一个第三方的库,结果在网上大肆搜罗相关的头文件以及实现,

    甚至一度弄得我崩溃(因为很难找全,可以想见,即使找到了相应的 .m 实现文件,也是其他人自己的实现而非标准的)~

    实际上,这个库只需要简单的写上一行代码 “#import <CommonCrypto/CommonCryptor.h>” 便可以使用了,

    这本身就是苹果开发sdk里面的东西,不需要去找.h和 .m文件,也不需要加入 dylib 啥的,直接用即可~


    2。还有就是,本文所涉及的 Base64 编码解码不是很完美,google toolbox for mac 框架里面

    也有对 Base64 编码解码的实现,用那个的话值得信赖一点。具体说来,要用到框架中的 Base64 功能模块的话,

    仅需将 “GTMDefines.h”、"GTMBase64.h"、"GTMBase64.m" 这三个文件加入到项目中即可

    (GTM 前缀即是  Google Toolbox for Mac 的缩写,google 哪来那么多精力给 mac 开发者写框架,疑惑不已)~

    另,Google 真的是了不起,突然间想起以前做 xml 解析的那个工具类也是 google 写出来的,

    不知不觉之间,收到 google 所带来的好处实在是多不胜数了:

    chrome,google-search,google-earth,etc。

    如果能进 google 工作,那可真是爽歪歪了(这么体贴的公司没理由不能做好做大,越来越强),哈哈,纯属意淫~


    3。objective-c 的 des 解码没有涉及到,个人使用之余,还是觉得很不方便,凑出了一个:

    -(NSString*) decryptUseDES:(NSString*)cipherText key:(NSString*)key {
        NSData* cipherData = [GTMBase64 decodeString:cipherText];
        unsigned char buffer[1024];
        memset(buffer, 0, sizeof(char));
        size_t numBytesDecrypted = 0;
        CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, 
                                              kCCAlgorithmDES, 
                                              kCCOptionPKCS7Padding, 
                                              [key UTF8String], 
                                              kCCKeySizeDES, 
                                              _iv_, 
                                              [cipherData bytes], 
                                              [cipherData length], 
                                              buffer, 
                                              1024, 
                                              &numBytesDecrypted);
        NSString* plainText = nil;
        if (cryptStatus == kCCSuccess) {
            NSData* data = [NSData dataWithBytes:buffer length:(NSUInteger)numBytesDecrypted];
            plainText = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
        }
        return plainText;
    }

    大体上应该是不存在什么问题的,我也测试了一下,发现能够正确地将密文还原为明文~

    终于是告已段落,发自内心的感慨,有时候一些问题的解决还真是得靠些运气。

    不过,经过今日的一番探索,让我发现,作为苹果开发者,竟然有如此之多的资源值得去挖掘,

    我想,在今后的开发旅途中,定会走的越来越平稳,越来越顺心~

    贴出一些个人觉得相当有参考价值的连接:

    http://www.helmsmansoft.com/index.php/archives/1179(本篇的原文链接)

    http://developer.apple.com/library/ios/navigation/(苹果开发可用库全辑录)

    http://blog.csdn.net/jasonyuan1986/article/details/6782304(GTMBase64)

    http://code.google.com/p/google-toolbox-for-mac/source/browse/trunk/Foundation/?r=87(GoogleToolboxForMac主页)


    最近做了一个移动项目,是有服务器和客户端类型的项目,客户端是要登录才行的,登录的密码要用DES加密,服务器是用Java开发的,客户端要同时支持多平台(Android、iOS),在处理iOS的DES加密的时候遇到了一些问题,起初怎么调都调不成和Android端生成的密文相同。最终一个忽然的想法让我找到了问题的所在,现在将代码总结一下,以备自己以后查阅。
    首先,Java端的DES加密的实现方式,代码如下:

    package org.bruce.vertices.controller.experiments;
    
    import javax.crypto.Cipher;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    
    public class DES {
    	private static byte[] iv = {1, 2, 3, 4, 5, 6, 7, 8};
    
    	public static String encryptDES(String encryptString, String encryptKey)
    			throws Exception {
    		IvParameterSpec zeroIv = new IvParameterSpec(iv);
    		SecretKeySpec key = new SecretKeySpec(encryptKey.getBytes(), "DES");
    		Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
    		cipher.init(Cipher.ENCRYPT_MODE, key, zeroIv);
    		byte[] encryptedData = cipher.doFinal(encryptString.getBytes());
    		return Base64.encode(encryptedData);
    	}
    
    	public static String decryptDES(String decryptString, String decryptKey)
    			throws Exception {
    		byte[] byteMi = Base64.decode(decryptString);
    		IvParameterSpec zeroIv = new IvParameterSpec(iv);
    		SecretKeySpec key = new SecretKeySpec(decryptKey.getBytes(), "DES");
    		Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
    		cipher.init(Cipher.DECRYPT_MODE, key, zeroIv);
    		byte decryptedData[] = cipher.doFinal(byteMi);
    
    		return new String(decryptedData);
    	}
    }

    上述代码用到了一个Base64的编码类,其代码的实现方式如下:

    package org.bruce.vertices.controller.experiments;
    
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    
    public class Base64 {
    	private static final char[] legalChars = 
    		"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
    
    	/**
    	 * data[]进行编码
    	 * 
    	 * @param data
    	 * @return
    	 */
    	public static String encode(byte[] data) {
    		int start = 0;
    		int len = data.length;
    		StringBuffer buf = new StringBuffer(data.length * 3 / 2);
    
    		int end = len - 3;
    		int i = start;
    		int n = 0;
    
    		while (i <= end) {
    			int d = ((((int) data[i]) & 0x0ff) << 16)
    					| ((((int) data[i + 1]) & 0x0ff) << 8)
    					| (((int) data[i + 2]) & 0x0ff);
    
    			buf.append(legalChars[(d >> 18) & 63]);
    			buf.append(legalChars[(d >> 12) & 63]);
    			buf.append(legalChars[(d >> 6) & 63]);
    			buf.append(legalChars[d & 63]);
    
    			i += 3;
    
    			if (n++ >= 14) {
    				n = 0;
    				buf.append(" ");
    			}
    		}
    
    		if (i == start + len - 2) {
    			int d = ((((int) data[i]) & 0x0ff) << 16)
    					| ((((int) data[i + 1]) & 255) << 8);
    
    			buf.append(legalChars[(d >> 18) & 63]);
    			buf.append(legalChars[(d >> 12) & 63]);
    			buf.append(legalChars[(d >> 6) & 63]);
    			buf.append("=");
    		} else if (i == start + len - 1) {
    			int d = (((int) data[i]) & 0x0ff) << 16;
    
    			buf.append(legalChars[(d >> 18) & 63]);
    			buf.append(legalChars[(d >> 12) & 63]);
    			buf.append("==");
    		}
    
    		return buf.toString();
    	}
    
    	public static byte[] decode(String s) {
    
    		ByteArrayOutputStream bos = new ByteArrayOutputStream();
    		try {
    			decode(s, bos);
    		} catch (IOException e) {
    			throw new RuntimeException();
    		}
    		byte[] decodedBytes = bos.toByteArray();
    		try {
    			bos.close();
    			bos = null;
    		} catch (IOException ex) {
    			System.err.println("Error while decoding BASE64: " + ex.toString());
    		}
    		return decodedBytes;
    	}
    
    	private static void decode(String s, OutputStream os) throws IOException {
    		int i = 0;
    
    		int len = s.length();
    
    		while (true) {
    			while (i < len && s.charAt(i) <= ' ')
    				i++;
    
    			if (i == len)
    				break;
    
    			int tri = (decode(s.charAt(i)) << 18)
    					+ (decode(s.charAt(i + 1)) << 12)
    					+ (decode(s.charAt(i + 2)) << 6)
    					+ (decode(s.charAt(i + 3)));
    
    			os.write((tri >> 16) & 255);
    			if (s.charAt(i + 2) == '=')
    				break;
    			os.write((tri >> 8) & 255);
    			if (s.charAt(i + 3) == '=')
    				break;
    			os.write(tri & 255);
    
    			i += 4;
    		}
    	}
    
    	private static int decode(char c) {
    		if (c >= 'A' && c <= 'Z')
    			return ((int) c) - 65;
    		else if (c >= 'a' && c <= 'z')
    			return ((int) c) - 97 + 26;
    		else if (c >= '0' && c <= '9')
    			return ((int) c) - 48 + 26 + 26;
    		else
    			switch (c) {
    			case '+':
    				return 62;
    			case '/':
    				return 63;
    			case '=':
    				return 0;
    			default:
    				throw new RuntimeException("unexpected code: " + c);
    			}
    	}
    }

    以上便是Java端的DES加密方法的全部实现过程。
    我还编写了一个将byte的二进制转换成16进制的方法,以便调试的时候使用打印输出加密后的byte数组的内容,这个方法不是加密的部分,只是为调试而使用的:

    package org.bruce.vertices.controller.experiments;
    
    /**
     * @author Bruce Yang
     * 连接:http://www.helmsmansoft.com/index.php/archives/1179
     * 我还编写了一个将byte的二进制转换成16进制的方法,
     * 以便调试的时候使用打印输出加密后的byte数组的内容,这个方法不是加密的部分,只是为调试而使用的:
     */
    public class ByteHexStrBridge {
    	
    	/**
    	 * 将二进制转换成16进制
    	 * @param buf
    	 * @return String
    	 */
    	public static String parseByte2HexStr(byte buf[]) {
    		StringBuffer sb = new StringBuffer();
    		for (int i = 0; i < buf.length; i++) {
    			String hex = Integer.toHexString(buf[i] & 0xFF);
    			if (hex.length() == 1) {
    				hex = '0' + hex;
    			}
    			sb.append(hex.toUpperCase());
    		}
    		return sb.toString();
    	}
    }

    下面是Objective-c在iOS上实现的DES加密算法: 添加头文件#import <CommonCrypto/CommonCryptor.h>

    - (NSString *) encryptUseDES:(NSString *)plainText key:(NSString *)key
    {
         NSString *ciphertext = nil;
         const char *textBytes = [plainText UTF8String];
        NSUInteger dataLength = [plainText length];
         unsigned char buffer[1024];
         memset(buffer, 0, sizeof(char));
         size_t numBytesEncrypted = 0;
         CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmDES,
                                                  kCCOptionPKCS7Padding,
                                                  [key UTF8String], kCCKeySizeDES,
                                                  iv,
                                                  textBytes, dataLength,
                                                  buffer, 1024,
                                                  &numBytesEncrypted);
        if (cryptStatus == kCCSuccess) {
             NSData *data = [NSData dataWithBytes:buffer length:(NSUInteger)numBytesEncrypted];
     
             ciphertext = [data base64Encoding];
             }
         return ciphertext;
    }

    下面是一个关键的类:NSData的Category实现,关于Category的实现网上很多说明不再讲述。

     static const char encodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
     - (NSString *)base64Encoding;
     {
     if (self.length == 0)
     return @"";
     
     char *characters = malloc(self.length*3/2);
     
     if (characters == NULL)
     return @"";
     
     int end = self.length - 3;
     int index = 0;
     int charCount = 0;
     int n = 0;
     
     while (index <= end) {
     int d = (((int)(((char *)[self bytes])[index]) & 0x0ff) << 16) 
     | (((int)(((char *)[self bytes])[index + 1]) & 0x0ff) << 8)
     | ((int)(((char *)[self bytes])[index + 2]) & 0x0ff);
     
     characters[charCount++] = encodingTable[(d >> 18) & 63];
     characters[charCount++] = encodingTable[(d >> 12) & 63];
     characters[charCount++] = encodingTable[(d >> 6) & 63];
     characters[charCount++] = encodingTable[d & 63];
     
     index += 3;
     
     if(n++ >= 14)
     {
     n = 0;
     characters[charCount++] = ' ';
     }
     }
     
     if(index == self.length - 2)
     {
     int d = (((int)(((char *)[self bytes])[index]) & 0x0ff) << 16) 
     | (((int)(((char *)[self bytes])[index + 1]) & 255) << 8);
     characters[charCount++] = encodingTable[(d >> 18) & 63];
     characters[charCount++] = encodingTable[(d >> 12) & 63];
     characters[charCount++] = encodingTable[(d >> 6) & 63];
     characters[charCount++] = '=';
     }
     else if(index == self.length - 1)
     {
     int d = ((int)(((char *)[self bytes])[index]) & 0x0ff) << 16;
     characters[charCount++] = encodingTable[(d >> 18) & 63];
     characters[charCount++] = encodingTable[(d >> 12) & 63];
     characters[charCount++] = '=';
     characters[charCount++] = '=';
     }
     NSString * rtnStr = [[NSString alloc] initWithBytesNoCopy:characters length:charCount encoding:NSUTF8StringEncoding freeWhenDone:YES];
    return rtnStr;
     }

    这个方法和java端的那个Base64的encode方法基本上是一个算法,只是根据语言的特点不同有少许的改动。
    下面也是Objective-c的一个二进制转换为16进制的方法,也是为了测试方便查看写的:

     +(NSString *) parseByte2HexString:(Byte *) bytes
     {
     NSMutableString *hexStr = [[NSMutableString alloc]init];
     int i = 0;
     if(bytes)
     {
     while (bytes[i] != '\0') 
     {
     NSString *hexByte = [NSString stringWithFormat:@"%x",bytes[i] & 0xff];///16进制数
     if([hexByte length]==1)
     [hexStr appendFormat:@"0%@", hexByte];
     else 
     [hexStr appendFormat:@"%@", hexByte];
     
     i++;
     }
     }
     NSLog(@"bytes 的16进制数为:%@",hexStr);
     return hexStr;
     }
     
     +(NSString *) parseByteArray2HexString:(Byte[]) bytes
     {
     NSMutableString *hexStr = [[NSMutableString alloc]init];
     int i = 0;
     if(bytes)
     {
     while (bytes[i] != '\0') 
     {
     NSString *hexByte = [NSString stringWithFormat:@"%x",bytes[i] & 0xff];///16进制数
     if([hexByte length]==1)
     [hexStr appendFormat:@"0%@", hexByte];
     else 
     [hexStr appendFormat:@"%@", hexByte];
     
     i++;
     }
     }
     NSLog(@"bytes 的16进制数为:%@",hexStr);
     return hexStr;
     }

    以上的加密方法所在的包是CommonCrypto/CommonCryptor.h。

    以上便实现了Objective-c和Java下在相同的明文和密钥的情况下生成相同明文的算法。
    Base64的算法可以用你们自己写的那个,不一定必须使用我提供的这个。解密的时候还要用Base64进行密文的转换。

    我的解密算法如下:

    	private static byte[] iv = {1, 2, 3, 4, 5, 6, 7, 8};
    
    	public static String decryptDES(String decryptString, String decryptKey)
    			throws Exception {
    		byte[] byteMi = Base64.decode(decryptString);
    		IvParameterSpec zeroIv = new IvParameterSpec(iv);
    		SecretKeySpec key = new SecretKeySpec(decryptKey.getBytes(), "DES");
    		Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
    		cipher.init(Cipher.DECRYPT_MODE, key, zeroIv);
    		byte decryptedData[] = cipher.doFinal(byteMi);
    
    		return new String(decryptedData);
    	}

    Base64的decode方法如下:

    	private static final char[] legalChars = 
    		"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
    
    	/**
    	 * data[]进行编码
    	 * 
    	 * @param data
    	 * @return
    	 */
    	public static String encode(byte[] data) {
    		int start = 0;
    		int len = data.length;
    		StringBuffer buf = new StringBuffer(data.length * 3 / 2);
    
    		int end = len - 3;
    		int i = start;
    		int n = 0;
    
    		while (i <= end) {
    			int d = ((((int) data[i]) & 0x0ff) << 16)
    					| ((((int) data[i + 1]) & 0x0ff) << 8)
    					| (((int) data[i + 2]) & 0x0ff);
    
    			buf.append(legalChars[(d >> 18) & 63]);
    			buf.append(legalChars[(d >> 12) & 63]);
    			buf.append(legalChars[(d >> 6) & 63]);
    			buf.append(legalChars[d & 63]);
    
    			i += 3;
    
    			if (n++ >= 14) {
    				n = 0;
    				buf.append(" ");
    			}
    		}
    
    		if (i == start + len - 2) {
    			int d = ((((int) data[i]) & 0x0ff) << 16)
    					| ((((int) data[i + 1]) & 255) << 8);
    
    			buf.append(legalChars[(d >> 18) & 63]);
    			buf.append(legalChars[(d >> 12) & 63]);
    			buf.append(legalChars[(d >> 6) & 63]);
    			buf.append("=");
    		} else if (i == start + len - 1) {
    			int d = (((int) data[i]) & 0x0ff) << 16;
    
    			buf.append(legalChars[(d >> 18) & 63]);
    			buf.append(legalChars[(d >> 12) & 63]);
    			buf.append("==");
    		}
    
    		return buf.toString();
    	}

    以上便实现了DES加密后的密文的解密。

    Java端的测试代码如下:

    package org.bruce.vertices.controller.experiments;
    
    import org.bruce.vertices.controller.interfaces.IGetVerticesMVC;
    
    public class DesTestCase implements IGetVerticesMVC {
    
    	/**
    	 * @param args
    	 */
    	public static void main(String[] args) throws Exception {
    		 String plainText = "abcd";
    		 String cipherText = DES.encryptDES(plainText, "20120401");
    		 System.out.println("明文:" + plainText);
    		 System.out.println("密钥:" + "20120401");
    		 System.out.println("密文:" + cipherText);
    		 System.out.println("解密后:" + DES.decryptDES(cipherText, "20120401"));
    		 
    		 String decryptedText = DES.decryptDES(cipherText, "20120401");
    		 System.err.println(decryptedText);
    	}
    }

    输出结果:

    明文:abcd
    密钥:20120401
    密文:W7HR43/usys=
    解密后:abcd

    Objective-c端的测试代码如下:

     NSString *plaintext = @"abcd";
     NSString *ciphertext = [EncryptUtil encryptUseDES:plaintext key:@"20120401"];
     NSLog(@"明文:%@",plaintext);
     NSLog(@"秘钥:%@",@"20120401");
     NSLog(@"密文:%@",ciphertext);

    输出结果:
    2012-04-05 12:00:47.348 TestEncrypt[806:f803] 明文:abcd
    2012-04-05 12:00:47.350 TestEncrypt[806:f803] 秘钥:20120401
    2012-04-05 12:00:47.350 TestEncrypt[806:f803] 密文:W7HR43/usys=


  • 相关阅读:
    开源项目中标准文件命名和实践
    linux远程拷贝命令-scp
    Linux访问Windows共享目录的方法——smbclient
    ADB Fix error : insufficient permissions for device
    APT典型应用示例
    我的参考书籍列表
    GCC Reference
    GNU make简介
    Windows下搭建Android NDK开发环境及命令行编译
    Git命令行基本操作
  • 原文地址:https://www.cnblogs.com/java20130723/p/3212289.html
Copyright © 2020-2023  润新知