zoukankan      html  css  js  c++  java
  • Base64编码

    写在前面

    今天在做一个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);
    }
    
  • 相关阅读:
    Java异常的深层次理解
    Java笔记1_java基础
    oracle怎么实现id自增和设置主键
    C51最小系统
    文本输入方式的pspice仿真步骤
    外部中断实验
    客户端测试
    定时器实验
    linklist template
    dfs:10元素取5个元素的组合数
  • 原文地址:https://www.cnblogs.com/dongkuo/p/5348508.html
Copyright © 2011-2022 走看看