近日工作上的原因,需要实现Java? AES算法和C语言下基于OpenSSL的AES 算法通信。这是个老问题了,网上搜到不少资料,但都不是很详细,没能解决问题。只能自己来了。
先说说AES算法。AES算法的实现有四种,如CBC/ECB/CFB/OFB,这四种Java和C都有实现。AES算法还有末尾的填充(padding),java支持的padding方式有三种NoPadding/PKCS5Padding/,而C却不能显式的设置padding方式,默认的padding就是在末尾加 '\0'。这是一个大坑,多少人都坑在这了。另外,网上很多JAVA AES算法,很多都用SecureRandom,如果你的代码中出现了SecureRandom这个东西,那么你再也不能用C解出来了。
先说Java端的。从良心上说,java的封装比C要强多了。先上代码:
? ? public static String encrypt(String content, String passwd) {
? ? ? ? try {
? ? ? ? ? ? Cipher aesECB = Cipher.getInstance("AES/ECB/PKCS5Padding");
? ? ? ? ? ? SecretKeySpec key = new SecretKeySpec(passwd.getBytes(), "AES");
? ? ? ? ? ? aesECB.init(Cipher.ENCRYPT_MODE, key);
? ? ? ? ? ? byte[] result = aesECB.doFinal(content.getBytes());
? ? ? ? ? ? return new BASE64Encoder().encode(result);
? ? ? ? } catch (NoSuchAlgorithmException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? } catch (NoSuchPaddingException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? } catch (InvalidKeyException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? } catch (IllegalBlockSizeException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? } catch (BadPaddingException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? return null;
? ? }
?
? ? public String decrypt(String content, String passwd) {
? ? ? ? try {
? ? ? ? ? ? Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");// 创建密码器
? ? ? ? ? ? SecretKeySpec key = new SecretKeySpec(passwd.getBytes(), "AES");
? ? ? ? ? ? cipher.init(Cipher.DECRYPT_MODE, key);// 初始化
? ? ? ? ? ? byte[] result = new BASE64Decoder().decodeBuffer(content);
? ? ? ? ? ? return new String(cipher.doFinal(result)); // 解密
? ? ? ? } catch (NoSuchAlgorithmException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? } catch (NoSuchPaddingException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? } catch (InvalidKeyException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? } catch (IllegalBlockSizeException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? } catch (BadPaddingException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? // TODO Auto-generated catch block
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? return null;
? ? }
以上就是两个加密解密函数,默认使用AES算法的ECB,填充方式选择了PKCS5Padding。中间用到了Base64算法将加密后的字串进行再加密,主要是为了可视化读和传递。使用Base64算法要引用sun.misc.BASE64Decoder和sun.misc.BASE64Encoder;
Java就是这么简单,当然它一开始并没有这么简单,我也是从SecureRandom里面跳出来的。
关于openssl库,先看EVP。EVP是OpenSSL自定义的一组高层算法封装函数,它是对具体算法的封装。使得可以在同一类加密算法框架下,通过相同的接口去调用不同的加密算法或者便利地改变具体的加密算法,这样大提高 了代码的可重用性。当你使用EVP的时候你就会发现,它的使用方法和Java是那么的相似,以至于会产生他们的结果肯定会相同的遐想。在使用它之前,我们先来学习学些它的用法。我们这里取出了几个重要的函数列在下面:
【EVP_CIPHER_CTX_init】
该函数初始化一个EVP_CIPHER_CTX结构体,只有初始化后该结构体才能在下面介绍的函数中使用。操作成功返回1,否则返回0。
【EVP_EncryptInit_ex】
该函数采用ENGINE参数impl的算法来设置并初始化加密结构体。其中,参数ctx必须在调用本函数之前已经进行了初始化。参数type通常通过函数类型来提供参数,如EVP_des_cbc函数的形式,即我们上一章中介绍的对称加密算法的类型。如果参数impl为NULL,那么就会使用缺省的实现算法。参数key是用来加密的对称密钥,iv参数是初始化向量(如果需要的话)。在算法中真正使用的密钥长度和初始化密钥长度是根据算法来决定的。在调用该函数进行初始化的时候,除了参数type之外,所有其它参数可以设置为NULL,留到以后调用其它函数的时候再提供,这时候参数type就设置为NULL就可以了。在缺省的加密参数不合适的时候,可以这样处理。操作成功返回1,否则返回0。
【EVP_EncryptUpdate】
该函数执行对数据的加密。该函数加密从参数in输入的长度为inl的数据,并将加密好的数据写入到参数out里面去。可以通过反复调用该函数来处理一个连续的数据块。写入到out的数据数量是由已经加密的数据的对齐关系决定的,理论上来说,从0到(inl+cipher_block_size-1)的任何一个数字都有可能(单位是字节),所以输出的参数out要有足够的空间存储数据。写入到out中的实际数据长度保存在outl参数中。操作成功返回1,否则返回0。
【EVP_EncryptFinal_ex】
该函数处理最后(Final)的一段数据。在函数在padding功能打开的时候(缺省)才有效,这时候,它将剩余的最后的所有数据进行加密处理。该算法使用标志的块padding方式(AKA PKCS padd