背景現象
服務之間的報文傳輸需要進行加密改造,發現上游發送過來表單請求(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();
}