写在前面

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

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

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

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

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

最开始以为是二者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编码的实现(末尾未加=):

  1. public static String base64Encode(String text) {
  2. if (text == null) {
  3. return null;
  4. }
  5. final String table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
  6. byte[] textBytes = text.getBytes();
  7. byte[] keyBytes = key.getBytes();
  8. int remain = 0, remainBitCount = 0;
  9. StringBuilder builder = new StringBuilder();
  10. for (int i = 0; i < textBytes.length; i++) {
  11. int b = textBytes[i] & 0xFF;
  12. // 获取b的高(6 - remainBitCount)位
  13. int hight = b >>> (2 + remainBitCount) & 0xFF;
  14. // 与上一轮的余留组合成一个字节
  15. int curr = remain << (6 - remainBitCount) | hight;
  16. // builder.append(table.charAt(curr));
  17. builder.append(table.charAt(curr));
  18. // 剩余b的低(2 + remainBitCount)位
  19. remain = hight << (2 + remainBitCount) ^ b;
  20. remainBitCount += 2;
  21. // 若剩余的位刚好为6位,则进行编码
  22. if (remainBitCount == 6) {
  23. builder.append(table.charAt(remain));
  24. remainBitCount = 0;
  25. remain = 0;
  26. }
  27. }
  28. if (remainBitCount != 0) {
  29. // 多出的位到末尾补0凑够6位
  30. builder.append(table.charAt(remain << (6 - remainBitCount)));
  31. }
  32. return builder.toString();
  33. }
  1. public static String base64Decode(String text) {
  2. if (text == null) {
  3. return text;
  4. }
  5. byte[] bs = new byte[text.length() * 6 / 8];
  6. int pos = 0, remain = 0, remainBitCount = 0;
  7. final String table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
  8. for (int i = 0; i < text.length(); i++) {
  9. int code = table.indexOf(text.charAt(i));
  10. if (remainBitCount + 6 < 8) {
  11. // 不足一个字节
  12. remain = remain << 6 | code;
  13. remainBitCount += 6;
  14. } else {
  15. // 足够一个字节,取code的高(8 - remainBitCount)位
  16. int hight = code >>> (remainBitCount - 2) & 0xFF;
  17. bs[pos] = (byte) (remain << (8 - remainBitCount) | hight);
  18. pos++;
  19. remainBitCount -= 2;
  20. // 保留剩余的位
  21. remain = hight << remainBitCount ^ code;
  22. }
  23. }
  24. return new String(bs);
  25. }

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. 页面中多个script块之间的关系

     一:函数声明与函数定义表达式在函数调用间的区别 <script type="text/javascript"> doA(); var doA = function(a ...

  2. angular2系列教程(六)两种pipe:函数式编程与面向对象编程

    今天,我们要讲的是angualr2的pipe这个知识点. 例子

  3. Linux学习之探索文件系统

    Linux,一起学习进步-    ls With it, we can see directory contents and determine a variety of important file ...

  4. potrace源码分析一

    1 简介 potrace是由Dalhousie University的Peter Selinger开发一款位图轮廓矢量化软件,该软件源码是可以公开下载的,详细见项目主页:http://potrace. ...

  5. WebApi返回Json格式字符串

    WebApi返回json格式字符串, 在网上能找到好几种方法, 其中有三种普遍的方法, 但是感觉都不怎么好. 先贴一下, 网上给的常用方法吧. 方法一:(改配置法) 找到Global.asax文件,在 ...

  6. ASP.NET MVC一次删除多笔记录

    批量删除数据记录,如下面的截屏: 先选中想删除的记录,然后点一下删除铵钮,系统将把选中的记录一次性删除.在此,Insus.NET不想每删除一笔记录连接一次数据库. 因此需要把选择的记录一次上传至服务器 ...

  7. Flexible 弹性盒子模型之CSS flex-basis 属性

    实例 设置第二个弹性盒元素的初始长度为 80 像素: div:nth-of-type(2){flex-basis:80px;}   效果预览 浏览器支持 表格中的数字表示支持该属性的第一个浏览器的版本 ...

  8. Javascript高性能编程-提高Dom访问速度

    在浏览器中对于Dom的操作和普通的脚本的操作处于两个不同的dll中,两个dll的交互是比较耗时的,优化对Dom的操作可以提高脚本的执行速度.下面是对如何优化的一些总结: 将需要多次操作的节点存储在一个 ...

  9. AEAI DP V3.7.0 发布,开源综合应用开发平台

    1  升级说明 AEAI DP 3.7版本是AEAI DP一个里程碑版本,基于JDK1.7开发,在本版本中新增支持Rest服务开发机制(默认支持WebService服务开发机制),且支持WS服务.RS ...

  10. 手把手教你做个人 app

    我们都知道,开发一个app很大程度依赖服务端:服务端提供接口数据,然后我们展示:另外,开发一个app,还需要美工协助切图.没了接口,没了美工,app似乎只能做成单机版或工具类app,真的是这样的吗?先 ...