java客户端和GO服务端联调错误

最近遇到一个bug很有意思,java客户端加密后传给服务端,服务端是GO Listen请求后解密处理,但是服务端解密失败。排查过程如下:

1.首先请求过程中有中间服务器转发,因此猜想可能是转发数据错误。由于发送数据为二进制流,因此对比客户端发送时与服务端接收到的数据的md5和base64,一致,排除中间传输错误。

2.再猜想是不是加密解密方法设置不一样导致失败。因为加密解密用的RSA,网上资料显示不同的RSA padding方式以及不同的provider都可能导致加密解密不一致从而解密失败,因此保证都是RSA/ECB/PKCS1Padding填充方式,而且java端两个provider(sun oracle实现和Bouncy Castle实现)都尝试下,还是有问题。

3.这时候没办法了,只能跟踪对比数据流了。在客户端发送二进制时计算数据md5和base64,分别发送二进制和base64数据,然后服务端收到二进制和base64,此时对二进制解密错误,但是对base64数据解码后再解密发现正确。这是为什么呢?对比收到的二进制数据和base64解码后的二进制,发现前者相对后者多了很多239,191,189,这些正是UTF8未识别字符的编码,因此问题就是客户端转二进制流时导致的问题。

下面用代码复现这一过程,

public static void EncodeAndDecode(String charset) throws UnsupportedEncodingException {
    	System.out.println("\r\n encode and decode with charset:" + charset);
    	
    	byte[] bytes = new byte[]{80, 75, 3, 4, 10, 60, 82, -83, 68, 8, 0, 28, 0, 80, 97, 121, 108, 108};
    	System.out.println(Arrays.toString(bytes));
    	
    	//先转成字符串
    	String s = new String(bytes, 0, bytes.length, charset);
    	//System.out.println(s);
    	
    	//字符串再反转成字节
    	byte[] bytes2 = s.getBytes(charset);
    	System.out.println(Arrays.toString(bytes2));
    }

UTF8的编码规则如下:

Unicode编码(16进制) UTF-8 字节流(二进制) 
000000 – 00007F 0xxxxxxx 
000080 – 0007FF 110xxxxx 10xxxxxx 
000800 – 00FFFF 1110xxxx 10xxxxxx 10xxxxxx 
010000 – 10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 

转码规则如下:

1.以0,110,1110,11110开头的字节,可以被识别
2.如果以10开头,则要求前一个字节必须以10,110,1110,11110开头
3.如果以10开头且前一个也以10开头,则再前一个必须以10,1110,11110开头
4.如果以10开头且前一个以10开头且再前一个也以10开头,则再前一个必须以11110开头
5.不能被识别的字节,每一个会转换成三个字节{11101111,10111111,10111101},也就是239,191,189

因此,实际编码中先将ASCLL二进制流转成字符串,如果这里是按照GBK编码转换再转回是没任何问题的。但是如果按照UTF-8转换时成字符串时,-83无法识别,会转成字节-17, -65, -67(其实就是239,191,189,因为java中byte-128~127)。

错误演示代码下载链接

原创,转载请注明来自http://blog.csdn.net/wenzhou1219

扫描二维码关注公众号,回复: 2670588 查看本文章

猜你喜欢

转载自blog.csdn.net/wenzhou1219/article/details/80501988