【bugfix】密文傳輸+UrlEncode的坑

背景現象

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章