背景现象
服务之间的报文传输需要进行加密改造,发现上游发送过来表单请求(application/x-www-form-urlencoded)中的SM4密文解密不了
原因
客户端和服务端打印密文如下:
客户端打印密文:
U000AAEAAAAyAAAAAgAAAEAAAADYnadFTDMUfEBTv/STdyFPyUwhDRbdI+syV3Z9yvFh/hCNCugs5FUoCs4++I2dUQY4T5wIehN86Jc9KjSvWXmF
U000AAEAAAAyAAAAAgAAAEAAAADYnadFTDMUfEBTv/STdyFPyUwhDRbdI syV3Z9yvFh/hCNCugs5FUoCs4 I2dUQY4T5wIehN86Jc9KjSvWXmF
服务端打印密文
加密串中的加号+,到达服务端之后消失了,变成了一个空格
是因为表单请求到tomcat中处理的时候,会对表单的字段名称和字段值进行一次UrlDecode
String name;
String value;
if (decodeName) {
urlDecode(tmpName);
}
tmpName.setCharset(charset);
name = tmpName.toString();
if (valueStart >= 0) {
if (decodeValue) {
urlDecode(tmpValue);
}
tmpValue.setCharset(charset);
value = tmpValue.toString();
} else {
value = "";
}
addParameter(name, value);
有69个字符:*+-./@_0-9a-zA-Z 不需要转码 ,算术加减乘除 4个,.小数点、@、 _下划线、数字10个、大小写26个字母 26*2
其余所有字符都将被替换成百分号后跟两位十六进制数,解码也按照相反的规则,有的不在码表中的会保持原样,但是+会被解成空格!!
解决方法
在客户端发送表单请求时,对表单字段的值进行UrlEncode
看一下业内最佳实践
如下是Spring-web中RestTemplate的实现,其中在写表单的时候进行了转码
URLEncoder.encode(name, charset.name()
restTemplate调用栈 |
---|
protected String serializeForm(MultiValueMap<String, String> formData, Charset charset) {
StringBuilder builder = new StringBuilder();
formData.forEach((name, values) ->
values.forEach(value -> {
try {
if (builder.length() != 0) {
builder.append('&');
}
builder.append(URLEncoder.encode(name, charset.name()));
if (value != null) {
builder.append('=');
builder.append(URLEncoder.encode(value, charset.name()));
}
}
catch (UnsupportedEncodingException ex) {
throw new IllegalStateException(ex);
}
}));
return builder.toString();
}