写在前面

今天在做一个Android app时遇到了一个问题:Android端采用ASE对称加密的数据在JavaWeb(jre1.8.0_7)后台解密时,居然解密失败了!经过测试后发现,对相同的数据,采用相同的密钥加密时,得到的密文是不同的,而加密的代码是完全一样的,只是在加密最后,对加密结果进行Base64编码时,API的调用略有不同。

Android上,ASE加密是这样写的:

public static String aesEncrypt(String content, String encryptKey) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom(encryptKey.getBytes()));
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(kgen.generateKey().getEncoded(), "AES"));
byte[] bs = cipher.doFinal(content.getBytes("utf-8"));
return Base64.encodeToString(bs, Base64.DEFAULT);
}

在普通JavaWeb上,ASE加密是这样写的:

public static String aesEncrypt(String content, String encryptKey) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom(encryptKey.getBytes()));
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(kgen.generateKey().getEncoded(), "AES"));
return Base64.getEncoder().encodeToString(cipher.doFinal(content.getBytes("utf-8")));
}

最开始以为是二者Base64编码标准不同造成的,于是去了解了一下Base64编码的原理,但最终发现其实二者加密得到的byte数组就不同,估计是标准不同吧。

下面是对Base64编码的学习记录,以及用Java实现Base64编码,主要参考了程晓晖的博客。

什么是Base64编码

首先,我们知道,编码的目的一般是为了数据的压缩,再就是数据传输的需要。Base64编码的产生就是因为后者:有些网络传送渠道并不支持所有的字节的传输。Base64编码就是一种将二进制数据映射成一些可打印字符(或逆过来)的映射规则,具体的说是映射到[A-Za-z0-9+/]上。比如将字符"中国"映射成5Lit5Zu9,当然也可将图片或其他文件编码成类似上述的字符。

Base64编码的原理

其次,我们需要知道的是,在计算机的世界里,一切都是由01011...这样的二进制数组成的,包括一个字符串,一张图片或一些其他类型的文件。比如对于一个字符串"中",在Java里,我们可以调用"中".getBytes()来得到"中"对应的字节数组:[-28, -72, -83],再将字节数组中的每个数转换为二进制,连接起来便是其二进制表示了:1110 0100_1011 1000_1010 1101(为了便于阅读,用下滑线将每个字节分开。当然,采用不同的编码形式,得到的字节数组可能不同,此处采用的是utf-8编码)。对于文件,可以从其输入流读中取到。

因为字符集合[A-Za-z0-9+/]长度是64,可以与所有6位长的二进制数一一对应 ,于是Base64编码的做法就是,将待编码的所有二进制位每6位分为一组,然后将每组二进制数映射到字符集合中的一个字符,最后将这些映射的字符依次排列起来便得到了编码结果。当然,待编码的二进制位可能不能被6整除,即最终可能会剩余2个或4个二进制位不能构成一组,这时我们可以在其后添加4或2个0来凑成一组。

比如对于上述的字符串"中",其二进制为:1110 0100_1011 1000_1010 1101,我们每6位组成一组,得到:111001_001011_100010_101101,每组转换为十进制就是:57_11_34_45,在按顺序映射到字符集合[A-Za-z0-9+/]上,便得到其Base64编码为:5Lit。如果最后一组填充了0,则还需在编码后的字符串后加上=符号。

解码的过程就是编码过程的逆过程,这里不再赘述。

Base64编码的实现

下面给出Base64编码的实现(末尾未加=):

public static String base64Encode(String text) {
if (text == null) {
return null;
}
final String table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
byte[] textBytes = text.getBytes();
byte[] keyBytes = key.getBytes();
int remain = 0, remainBitCount = 0;
StringBuilder builder = new StringBuilder();
for (int i = 0; i < textBytes.length; i++) {
int b = textBytes[i] & 0xFF;
// 获取b的高(6 - remainBitCount)位
int hight = b >>> (2 + remainBitCount) & 0xFF;
// 与上一轮的余留组合成一个字节
int curr = remain << (6 - remainBitCount) | hight;
// builder.append(table.charAt(curr));
builder.append(table.charAt(curr));
// 剩余b的低(2 + remainBitCount)位
remain = hight << (2 + remainBitCount) ^ b;
remainBitCount += 2;
// 若剩余的位刚好为6位,则进行编码
if (remainBitCount == 6) {
builder.append(table.charAt(remain));
remainBitCount = 0;
remain = 0;
}
}
if (remainBitCount != 0) {
// 多出的位到末尾补0凑够6位
builder.append(table.charAt(remain << (6 - remainBitCount)));
}
return builder.toString();
}
public static String base64Decode(String text) {
if (text == null) {
return text;
}
byte[] bs = new byte[text.length() * 6 / 8];
int pos = 0, remain = 0, remainBitCount = 0;
final String table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
for (int i = 0; i < text.length(); i++) {
int code = table.indexOf(text.charAt(i));
if (remainBitCount + 6 < 8) {
// 不足一个字节
remain = remain << 6 | code;
remainBitCount += 6;
} else {
// 足够一个字节,取code的高(8 - remainBitCount)位
int hight = code >>> (remainBitCount - 2) & 0xFF;
bs[pos] = (byte) (remain << (8 - remainBitCount) | hight);
pos++;
remainBitCount -= 2;
// 保留剩余的位
remain = hight << remainBitCount ^ code;
}
}
return new String(bs);
}

Base64编码的更多相关文章

  1. URL安全的Base64编码

    Base64编码可用于在HTTP环境下传递较长的标识信息.在其他应用程序中,也常常需要把二进制数据编码为适合放在URL(包括隐藏表单域)中的形式.此时,采用Base64编码不仅比较简短,同时也具有不可 ...

  2. Android数据加密之Base64编码算法

    前言: 前面学习总结了平时开发中遇见的各种数据加密方式,最终都会对加密后的二进制数据进行Base64编码,起到一种二次加密的效果,其实呢Base64从严格意义上来说的话不是一种加密算法,而是一种编码算 ...

  3. 网络安全——Base64编码、MD5、SHA1-SHA512、HMAC(SHA1-SHA512)哈希

    据说今天520是个好日子,为什么我想起的是502.500.404这些?还好服务器没事! 一.Base64编码 Base64编码要求把3个8位字节(3*8=24)转化为4个6位的字节(4*6=24),之 ...

  4. Base64编码【转】

    转http://www.cnblogs.com/luguo3000/p/3940197.html 开发者对Base64编码肯定很熟悉,是否对它有很清晰的认识就不一定了.实际上Base64已经简单到不能 ...

  5. 【前端攻略】:玩转图片Base64编码

    引言 图片处理在前端工作中可谓占据了很重要的一壁江山.而图片的 base64 编码可能相对一些人而言比较陌生,本文不是从纯技术的角度去讨论图片的 base64 编码.标题略大,不过只是希望通过一些浅显 ...

  6. 为什么要用base64编码

    1.需求 了解为什么要使用base64对数据编码 2.理由 因为传输二进制数据的时候,网络中间的有些路由会把ascii码中的不可见字符删了,导致数据不一致.一般也会对url进行base64编码 Whe ...

  7. 图片Base64编码

    我们经常在做Jquery插件的时候需要插入一些自定义的控件,比如说按钮,而我们自己又觉着button标签很丑,而且不同浏览器显示的效果还不一样,这个时候我们需要用到图片,当然,我们可以通过img标签添 ...

  8. 浅析用Base64编码的图片优化网页加载速度

    想必大家都知道网页加载的过程,从开始请求,到加载页面,开始解析和显示网页,遇到图片就再次向服务器发送请求,加载图片.如果图片很多的话,就会产生大量的http请求,从而影响页面的加载速度.所以现在有一种 ...

  9. Base64编码原理分析

    Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一,在了解Base64编码之前,先了解几个基本概念:位.字节. 位:"位(bit)"是计算机中最小的数据单位.每一位 ...

随机推荐

  1. ASP.NET Core 之 Identity 入门(一)

    前言 在 ASP.NET Core 中,仍然沿用了 ASP.NET里面的 Identity 组件库,负责对用户的身份进行认证,总体来说的话,没有MVC 5 里面那么复杂,因为在MVC 5里面引入了OW ...

  2. 学习AOP之认识一下Spring AOP

    心碎之事 要说知道AOP这个词倒是很久很久以前了,但是直到今天我也不敢说非常的理解它,其中的各种概念即抽象又太拗口. 在几次面试中都被问及AOP,但是真的没有答上来,或者都在面上,这给面试官的感觉就是 ...

  3. 在.NET Core 里使用 BouncyCastle 的DES加密算法

    .NET Core上面的DES等加密算法要等到1.2 才支持,我们可是急需这个算法的支持,文章<使用 JavaScriptService 在.NET Core 里实现DES加密算法>需要用 ...

  4. Kotlin的Lambda表达式以及它们怎样简化Android开发(KAD 07)

    作者:Antonio Leiva 时间:Jan 5, 2017 原文链接:https://antonioleiva.com/lambdas-kotlin/ 由于Lambda表达式允许更简单的方式建模式 ...

  5. Spring之旅

    Java使得以模块化构建复杂应用系统成为可能,它为Applet而来,但为组件化而留. Spring是一个开源的框架,最早由Rod Johnson创建.Spring是为了解决企业级应用开发的复杂性而创建 ...

  6. Java 字符串格式化详解

    Java 字符串格式化详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 文中如有纰漏,欢迎大家留言指出. 在 Java 的 String 类中,可以使用 format() 方法 ...

  7. UWP开发必备以及常用知识点总结

    一直在学UWP,一直在写Code,自己到达了什么水平?还有多少东西需要学习才能独挡一面?我想对刚接触UWP的开发者都有这种困惑,偶尔停下来总结分析一下还是很有收获的! 以下内容是自己开发中经常遇到的一 ...

  8. Javacript实现字典结构

    字典是一种用[键,值]形式存储元素的数据结构.也称作映射,ECMAScript6中,原生用Map实现了字典结构. 下面代码是尝试用JS的Object对象来模拟实现一个字典结构. <script& ...

  9. 微软发布VSBT,无需安装Visual Studio即可实现项目编译

    安装了Visual Studio的那些使用微软平台的开发者通常能够非常容易地操作自己的项目:打开解决方案,修改内容,设置好所有必须的文件以及配置后编译项目.但是在构建服务器或者持续交付系统等没有安装V ...

  10. 分布式学习系列【dubbo入门实践】

    分布式学习系列[dubbo入门实践] dubbo架构 组成部分:provider,consumer,registry,monitor: provider,consumer注册,订阅类似于消息队列的注册 ...