1、關鍵詞java
HTTP,HTTPS,AES,SHA-1,MD5,消息摘要,數字簽名,數字加密,Java,Servlet,Bouncy Castlegit
2、名詞解釋算法
數字摘要:是將任意長度的消息變成固定長度的短消息,它相似於一個自變量是消息的函數,也就是Hash函數。數字摘要就是採用單項Hash函數將須要加密的明文「摘要」成一串固定長度(128位)的密文這一串密文又稱爲數字指紋,它有固定的長度,並且不一樣的明文摘要成密文,其結果老是不一樣的,而一樣的明文其摘要一定一致。spring
AES:密碼學中的高級加密標準(Advanced Encryption Standard,AES),又稱Rijndael加密法,是美國聯邦政府採用的一種區塊加密標準。這個標準用來替代原先的DES,已經被多方分析且廣爲全世界所使用。是一種對稱加密算法。apache
SHA-1:安全哈希算法(Secure Hash Algorithm)主要適用於數字簽名標準 (Digital Signature Standard DSS)裏面定義的數字簽名算法(Digital Signature Algorithm DSA)。 SHA1有以下特性:不能夠從消息摘要中復原信息;兩個不一樣的消息不會產生一樣的消息摘要。安全
MD5:Message Digest Algorithm MD5(中文名爲消息摘要算法第五版)爲計算機安全領域普遍使用的一種散列函數,用以提供消息的完整性保護。服務器
3、項目背景微信
某合做公司須要經過互聯網向我司傳遞一些用戶數據,可是我所在項目組的外網服務器上並沒有部署https,只能基於http進行數據傳輸。爲了保護雙方共同的用戶數據,必須對在互聯網上傳輸的信息進行加密處理。app
4、方案設計微信公衆平臺
這裏涉及到兩個問題,一是採用什麼樣的遠程消息傳遞框架,二是如何對傳輸的數據進行加密。
本人平時開發所用的語言主要是Java,對於Jsp/Servlet還比較熟悉,結合去年參加過所在公司的微信公衆號開發的經驗,設計出了以下方案:
1.在客戶端採用構造http post請求,把用戶數據加密後放入request body中,並在http參數中放入調用方的簽名;
2.服務端接收到請求,提取參數進行簽名校驗,經過後從request body中提取密文進行解密,而後進行後續處理,最終生成響應返回給客戶端。
如下是具體處理的流程圖:
在數據加密階段,基於性能以及效率考慮,採用了Bouncy Castle提供的AES算法,而生成簽名則採用了jdk提供的SHA-1,值得注意的是,基於安全考慮,消息密文的消息摘要也被列入到參與數字簽名的參數之一。
5、代碼實現
1.AES加密工具類:
- import org.apache.commons.lang.StringUtils;
- import org.apache.log4j.Logger;
- import org.bouncycastle.crypto.CipherParameters;
- import org.bouncycastle.crypto.engines.AESFastEngine;
- import org.bouncycastle.crypto.modes.CBCBlockCipher;
- import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
- import org.bouncycastle.crypto.params.KeyParameter;
- import org.bouncycastle.crypto.params.ParametersWithIV;
- import org.bouncycastle.util.encoders.Hex;
- /**
- * AES encryption and decryption tool.
- *
- * @author ben
- * @creation 2014年3月20日
- */
- public class AESTool {
- protected static final Logger log = Logger.getLogger(AESTool.class);
- private byte[] initVector = { 0x32, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31,
- 0x38, 0x27, 0x36, 0x35, 0x33, 0x23, 0x32, 0x31 };
- /**
- * FIXME For demo only, should rewrite this method in your product environment!
- *
- * @param appid
- * @return
- */
- public String findKeyById(String appid) {
- // Fake key.
- String key = "123456789012345678901234567890~!";
- return key;
- }
- /**
- * Encrypt the content with a given key using aes algorithm.
- *
- * @param content
- * @param key
- * must contain exactly 32 characters
- * @return
- * @throws Exception
- */
- public String encrypt(String content, String key) throws Exception {
- if (key == null) {
- throw new IllegalArgumentException("Key cannot be null!");
- }
- String encrypted = null;
- byte[] keyBytes = key.getBytes();
- if (keyBytes.length != 32 && keyBytes.length != 24
- && keyBytes.length != 16) {
- throw new IllegalArgumentException(
- "Key length must be 128/192/256 bits!");
- }
- byte[] encryptedBytes = null;
- encryptedBytes = encrypt(content.getBytes(), keyBytes, initVector);
- encrypted = new String(Hex.encode(encryptedBytes));
- return encrypted;
- }
- /**
- * Decrypt the content with a given key using aes algorithm.
- *
- * @param content
- * @param key
- * must contain exactly 32 characters
- * @return
- * @throws Exception
- */
- public String decrypt(String content, String key) throws Exception {
- if (key == null) {
- throw new IllegalArgumentException("Key cannot be null!");
- }
- String decrypted = null;
- byte[] encryptedContent = Hex.decode(content);
- byte[] keyBytes = key.getBytes();
- byte[] decryptedBytes = null;
- if (keyBytes.length != 32 && keyBytes.length != 24
- && keyBytes.length != 16) {
- throw new IllegalArgumentException(
- "Key length must be 128/192/256 bits!");
- }
- decryptedBytes = decrypt(encryptedContent, keyBytes, initVector);
- decrypted = new String(decryptedBytes);
- return decrypted;
- }
- /**
- * Encrypt data.
- *
- * @param plain
- * @param key
- * @param iv
- * @return
- * @throws Exception
- */
- public byte[] encrypt(byte[] plain, byte[] key, byte[] iv) throws Exception {
- PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher(
- new CBCBlockCipher(new AESFastEngine()));
- CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(key),
- iv);
- aes.init(true, ivAndKey);
- return cipherData(aes, plain);
- }
- /**
- * Decrypt data.
- *
- * @param cipher
- * @param key
- * @param iv
- * @return
- * @throws Exception
- */
- public byte[] decrypt(byte[] cipher, byte[] key, byte[] iv)
- throws Exception {
- PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher(
- new CBCBlockCipher(new AESFastEngine()));
- CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(key),
- iv);
- aes.init(false, ivAndKey);
- return cipherData(aes, cipher);
- }
- /**
- * Encrypt or decrypt data.
- *
- * @param cipher
- * @param data
- * @return
- * @throws Exception
- */
- private byte[] cipherData(PaddedBufferedBlockCipher cipher, byte[] data)
- throws Exception {
- int minSize = cipher.getOutputSize(data.length);
- byte[] outBuf = new byte[minSize];
- int length1 = cipher.processBytes(data, 0, data.length, outBuf, 0);
- int length2 = cipher.doFinal(outBuf, length1);
- int actualLength = length1 + length2;
- byte[] result = new byte[actualLength];
- System.arraycopy(outBuf, 0, result, 0, result.length);
- return result;
- }
- public static void main(String[] args) throws Exception {
- AESTool aesTool = new AESTool();
- String appid = "canairport001";
- String key = aesTool.findKeyById(appid);
- String xml = "<root><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name></root>";
- String encrypted = aesTool.encrypt(xml, key);
- System.out.println("encrypted: \n" + encrypted);
- System.out.println("encrypted length: \n" + encrypted.length());
- String decrypted = aesTool.decrypt(encrypted, key);
- System.out.println("decrypted: \n" + decrypted);
- System.out.println("decrypted length: \n" + decrypted.length());
- boolean isSuccessful = StringUtils.equals(decrypted, xml);
- System.out.println(isSuccessful);
- }
- }
2.數字簽名工具類:
- import java.security.MessageDigest;
- import java.security.NoSuchAlgorithmException;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.List;
- import org.apache.commons.lang.StringUtils;
- import org.apache.log4j.Logger;
- /**
- * @author lixuanbin
- * @creation 2013-1-30
- */
- public class SignatureUtil {
- protected static Logger log = Logger.getLogger(SignatureUtil.class);
- private static final char[] hexArray = "0123456789ABCDEF".toCharArray();
- private String encryptionAlgorithm = "SHA-1";
- public String bytesToHexString(byte[] bytes) {
- char[] hexChars = new char[bytes.length * 2];
- for (int j = 0; j < bytes.length; j++) {
- int v = bytes[j] & 0xFF;
- hexChars[j * 2] = hexArray[v >>> 4];
- hexChars[j * 2 + 1] = hexArray[v & 0x0F];
- }
- return new String(hexChars);
- }
- public byte[] hexStringToBytes(String s) {
- int len = s.length();
- byte[] data = new byte[len / 2];
- for (int i = 0; i < len; i += 2) {
- data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character
- .digit(s.charAt(i + 1), 16));
- }
- return data;
- }
- /**
- * 使用指定算法生成消息摘要,默認是md5
- *
- * @param strSrc
- * , a string will be encrypted; <br/>
- * @param encName
- * , the algorithm name will be used, dafault to "MD5"; <br/>
- * @return
- */
- public String digest(String strSrc, String encName) {
- MessageDigest md = null;
- String strDes = null;
- byte[] bt = strSrc.getBytes();
- try {
- if (encName == null || encName.equals("")) {
- encName = "MD5";
- }
- md = MessageDigest.getInstance(encName);
- md.update(bt);
- strDes = bytesToHexString(md.digest()); // to HexString
- } catch (NoSuchAlgorithmException e) {
- log.error("Invalid algorithm: " + encName);
- return null;
- }
- return strDes;
- }
- /**
- * 根據appid、token、lol以及時間戳來生成簽名
- *
- * @param appid
- * @param token
- * @param lol
- * @param millis
- * @return
- */
- public String generateSignature(String appid, String token, String lol,
- long millis) {
- String timestamp = String.valueOf(millis);
- String signature = null;
- if (StringUtils.isNotBlank(token) && StringUtils.isNotBlank(timestamp)
- && StringUtils.isNotBlank(appid)) {
- List<String> srcList = new ArrayList<String>();
- srcList.add(timestamp);
- srcList.add(appid);
- srcList.add(token);
- srcList.add(lol);
- // 按照字典序逆序拼接參數
- Collections.sort(srcList);
- Collections.reverse(srcList);
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < srcList.size(); i++) {
- sb.append(srcList.get(i));
- }
- signature = digest(sb.toString(), encryptionAlgorithm);
- srcList.clear();
- srcList = null;
- }
- return signature;
- }
- /**
- * 驗證簽名: <br/>
- * 1.根據appid獲取該渠道的token;<br/>
- * 2.根據appid、token、lol以及時間戳計算一次簽名;<br/>
- * 3.比較傳過來的簽名以及計算出的簽名是否一致;
- * @param signature
- * @param appid
- * @param lol
- * @param millis
- * @return
- */
- public boolean isValid(String signature, String appid, String lol,
- long millis) {
- String token = findTokenById(appid);
- String calculatedSignature = generateSignature(appid, token, lol,
- millis);
- log.info("calculated signature: \n" + calculatedSignature);
- if (StringUtils.equals(calculatedSignature, signature)) {
- return true;
- } else {
- return false;
- }
- }
- /**
- * FIXME For demo only, should be a different string in production.
- * @param appid
- * @return
- */
- public String findTokenById(String appid) {
- String token = "#@!1234567890!@#";
- return token;
- }
- public static void main(String[] args) {
- SignatureUtil generator = new SignatureUtil();
- String xmlString = "<root><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name></root>";
- System.out.println(xmlString.getBytes().length);
- String digest = generator.digest(xmlString, "MD5");
- System.out.println(digest);
- System.out.println(digest.getBytes().length);
- String appid = "canairport001";
- String token = generator.findTokenById(appid);
- long millis = System.currentTimeMillis();
- String signature = generator.generateSignature(appid, token, digest,
- millis);
- System.out.println(signature);
- boolean isValid = generator.isValid(signature, appid, digest, millis);
- System.out.println(isValid);
- }
- }
3.發送方代碼:
- import java.io.IOException;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.Map;
- import java.util.Map.Entry;
- import org.apache.commons.lang.StringUtils;
- import org.apache.http.HttpEntity;
- import org.apache.http.HttpHost;
- import org.apache.http.HttpResponse;
- import org.apache.http.HttpStatus;
- import org.apache.http.auth.AuthScope;
- import org.apache.http.auth.UsernamePasswordCredentials;
- import org.apache.http.client.ClientProtocolException;
- import org.apache.http.client.methods.HttpPost;
- import org.apache.http.conn.params.ConnRoutePNames;
- import org.apache.http.entity.StringEntity;
- import org.apache.http.impl.client.DefaultHttpClient;
- import org.apache.http.message.BasicHeader;
- import org.apache.http.protocol.HTTP;
- import org.apache.http.util.EntityUtils;
- import org.apache.log4j.Logger;
- /**
- * @author ben
- * @creation 2014年6月9日
- */
- public class HttpclientUtil {
- protected static final Logger log = Logger.getLogger(HttpclientUtil.class);
- /**
- * 根據傳入的uri和參數map拼接成實際uri
- *
- * @param uri
- * @param paraMap
- * @return
- */
- public String buildUri(String uri, Map<String, String> paraMap) {
- StringBuilder sb = new StringBuilder();
- uri = StringUtils.trim(uri);
- uri = StringUtils.removeEnd(uri, "/");
- uri = StringUtils.removeEnd(uri, "?");
- sb.append(uri);
- if (paraMap != null && !paraMap.isEmpty()) {
- sb.append("?");
- Iterator<Entry<String, String>> iterator = paraMap.entrySet()
- .iterator();
- while (iterator.hasNext()) {
- Map.Entry<String, String> pair = iterator.next();
- try {
- String keyString = pair.getKey();
- String valueString = pair.getValue();
- sb.append(keyString);
- sb.append("=");
- sb.append(valueString);
- sb.append("&");
- } catch (Exception e) {
- log.error(e, e);
- }
- }
- }
- return StringUtils.removeEnd(sb.toString(), "&");
- }
- /**
- * Post an xml string to a specific host.
- *
- * @param targetHost
- * @param targetPort
- * @param protocol
- * @param proxyHost
- * @param proxyPort
- * @param proxyUser
- * @param proxyPassword
- * @param uri
- * @param paraMap
- * @param xml
- * @param charset
- * @return
- * @throws ClientProtocolException
- * @throws IOException
- */
- public String postXmlString(String targetHost, int targetPort,
- String protocol, String proxyHost, int proxyPort, String proxyUser,
- String proxyPassword, String uri, Map<String, String> paraMap,
- String xml, String charset) throws ClientProtocolException,
- IOException {
- String result = null;
- DefaultHttpClient httpclient = new DefaultHttpClient();
- if (StringUtils.isNotBlank(proxyHost) && proxyPort > 0) {
- // 設置上網代理
- AuthScope authScope = new AuthScope(proxyHost, proxyPort);
- if (StringUtils.isNotBlank(proxyUser)
- && StringUtils.isNotBlank(proxyPassword)) {
- // 設置上網代理的用戶名和密碼
- UsernamePasswordCredentials upc = new UsernamePasswordCredentials(
- proxyUser, proxyPassword);
- httpclient.getCredentialsProvider().setCredentials(authScope,
- upc);
- }
- HttpHost proxy = new HttpHost(proxyHost, proxyPort);
- httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,
- proxy);
- }
- HttpHost host = new HttpHost(targetHost, targetPort, protocol);
- uri = buildUri(uri, paraMap);
- log.info("post uri: " + uri);
- log.info("post content: " + xml);
- HttpPost post = new HttpPost(uri);
- StringEntity se = new StringEntity(xml,
- StringUtils.isNotBlank(charset) ? charset : "utf-8");
- se.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE,
- "application/xml"));
- post.setEntity(se);
- HttpResponse response = httpclient.execute(host, post);
- if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode()) {
- HttpEntity entity = response.getEntity();
- if (entity != null) {
- result = EntityUtils.toString(entity);
- log.info("post result: " + result);
- }
- } else {
- log.error("post failed, status code: "
- + response.getStatusLine().getStatusCode());
- }
- return result;
- }
- public static void main(String[] args) throws Exception {
- AESTool aes = new AESTool();
- SignatureUtil signatureUtil = new SignatureUtil();
- String appid = "canairport001";
- String token = signatureUtil.findTokenById(appid);
- String key = aes.findKeyById(appid);
- long millis = System.currentTimeMillis();
- String xml = "<dependency><groupId>commons-lang</groupId><artifactId>commons-lang</artifactId><version>2.5</version></dependency>";
- xml = aes.encrypt(xml, key);
- String lol = signatureUtil.digest(xml, "MD5");
- String signature = signatureUtil.generateSignature(appid, token, lol,
- millis);
- log.info("lol: \n" + lol);
- log.info("signature: \n" + signature);
- String uri = "http://127.0.0.1:8080/demo/psginfo.do";
- Map<String, String> paraMap = new HashMap<String, String>();
- paraMap.put("s", signature);
- paraMap.put("a", appid);
- paraMap.put("t", String.valueOf(millis));
- paraMap.put("l", lol);
- paraMap.put("o", "test");
- HttpclientUtil util = new HttpclientUtil();
- try {
- String result = util.postXmlString("127.0.0.1", 8080, "http", null,
- 0, null, null, uri, paraMap, xml, "utf-8");
- result = aes.decrypt(result, key);
- System.out.println(result);
- } catch (ClientProtocolException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
4.服務端代碼:
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.io.PrintWriter;
- import java.io.UnsupportedEncodingException;
- import javax.servlet.ServletException;
- import javax.servlet.annotation.WebServlet;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.apache.commons.lang.StringUtils;
- import org.apache.log4j.Logger;
- import co.speedar.wechat.util.AESTool;
- import co.speedar.wechat.util.SignatureUtil;
- /**
- * Servlet implementation class PsginfoServlet
- */
- @WebServlet(urlPatterns = { "/psginfo.do" }, loadOnStartup = 1)
- public class PsginfoServlet extends HttpServlet {
- protected static final Logger log = Logger.getLogger(PsginfoServlet.class);
- private static final long serialVersionUID = 6536688299231165548L;
- private SignatureUtil signatureUtil = new SignatureUtil();
- private AESTool aes = new AESTool();
- /**
- * @see HttpServlet#HttpServlet()
- */
- public PsginfoServlet() {
- super();
- }
- /**
- * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
- * response)
- */
- protected void doGet(HttpServletRequest request,
- HttpServletResponse response) throws ServletException, IOException {
- String echostr = request.getParameter("e");
- log.info("echostr before echo: " + echostr);
- String signature = request.getParameter("s");
- String appid = request.getParameter("a");
- String timestamp = request.getParameter("t");
- String lol = request.getParameter("l");
- long millis = Long.valueOf(timestamp);
- // Need to check signature in product mode.
- if (signatureUtil.isValid(signature, appid, lol, millis)) {
- PrintWriter writer = response.getWriter();
- log.info("echostr after echo: " + echostr);
- writer.print(echostr);
- writer.flush();
- writer.close();
- }
- }
- /**
- * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
- * response)
- */
- protected void doPost(HttpServletRequest request,
- HttpServletResponse response) throws ServletException, IOException {
- // Get request parameters.
- String signature = request.getParameter("s");
- String appid = request.getParameter("a");
- String timestamp = request.getParameter("t");
- String lol = request.getParameter("l");
- String operation = request.getParameter("o");
- long millis = Long.valueOf(timestamp);
- // Get xml data.
- String encoding = StringUtils
- .isNotBlank(request.getCharacterEncoding()) ? request
- .getCharacterEncoding() : "utf-8";
- String requestXmlString = getXmlStringFromHttpRequest(request);
- String digest = signatureUtil.digest(requestXmlString, "MD5");
- // Check signature and digest.
- if (StringUtils.equals(digest, lol)) {
- if (signatureUtil.isValid(signature, appid, lol, millis)) {
- try {
- String key = aes.findKeyById(appid);
- requestXmlString = aes.decrypt(requestXmlString, key);
- log.info("received xml data:\n" + requestXmlString);
- // 校驗xml合法性並執行相應動做
- String responseXmlString = doSomeThing(requestXmlString,
- operation);
- responseXmlString = aes.encrypt(responseXmlString, key);
- log.info("responsed xml data:\n" + responseXmlString);
- response.setCharacterEncoding(encoding);
- PrintWriter writer = response.getWriter();
- writer.print(responseXmlString);
- writer.flush();
- writer.close();
- } catch (Exception e) {
- log.error(e, e);
- }
- } else {
- log.error("invalid signature");
- }
- } else {
- log.error("invalid digest.");
- }
- }
- /**
- * TODO Write your own business here.
- *
- * @param xml
- * @param operation
- * @return
- */
- private String doSomeThing(String xml, String operation) {
- return "done";
- }
- /**
- * Extract xml string form http request.
- *
- * @param request
- * @return
- * @throws IOException
- */
- private String getXmlStringFromHttpRequest(HttpServletRequest request) {
- String requestXmlString = "";
- try {
- InputStream inputStream = request.getInputStream();
- String encoding = StringUtils.isNotBlank(request
- .getCharacterEncoding()) ? request.getCharacterEncoding()
- : "utf-8";
- requestXmlString = getXmlStringFromInputStream(inputStream,
- encoding);
- encoding = null;
- inputStream.close();
- inputStream = null;
- } catch (IOException e) {
- log.error(e, e);
- }
- return requestXmlString;
- }
- /**
- * Extract xml string from the inputStream.
- *
- * @param inputStream
- * @param charsetName
- * @return
- */
- private String getXmlStringFromInputStream(InputStream inputStream,
- String charsetName) {
- String resultXmlString = "";
- String tempString = null;
- BufferedReader bufferedReader;
- try {
- bufferedReader = new BufferedReader(new InputStreamReader(
- inputStream, charsetName));
- tempString = bufferedReader.readLine();
- while (tempString != null) {
- resultXmlString += tempString;
- tempString = bufferedReader.readLine();
- }
- tempString = null;
- bufferedReader.close();
- bufferedReader = null;
- } catch (UnsupportedEncodingException e) {
- log.error(e, e);
- } catch (IOException e) {
- log.error(e, e);
- }
- return StringUtils.trim(resultXmlString);
- }
- }
5.maven配置:
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk16</artifactId>
- <version>1.46</version>
- </dependency>
- <dependency>
- <groupId>commons-lang</groupId>
- <artifactId>commons-lang</artifactId>
- <version>2.5</version>
- </dependency>
- <dependency>
- <groupId>org.apache.httpcomponents</groupId>
- <artifactId>httpclient</artifactId>
- <version>4.2.5</version>
- </dependency>
- <dependency>
- <groupId>org.apache.httpcomponents</groupId>
- <artifactId>httpmime</artifactId>
- <version>4.2.5</version>
- </dependency>
6、結語
在本方案設計實現過程當中,消息傳遞的框架採用的是Java開發者所熟悉的Servlet技術,摘要、簽名、加密所採用的算法,以及所依賴的第三方jar也是比較有口碑又大衆化的貨,對於有相似須要的開發者來講,本方案具備必定的參考意義。遠程傳遞消息框架以及生成簽名的環節,主要是模仿了微信公衆平臺的消息交互方式以及生成簽名的思路,而有所創新的一小點是,把消息密文的MD5值也參與到了簽名運算中,增長了被仿冒的難度,同時也便於服務方校驗消息在傳遞過程當中是否有被第三方所篡改。
基於簡化工程配置的考慮,本示例項目中沒有使用spring,您能夠在您的生產項目中把本示例中的代碼改形成春哥的單例業務bean。密鑰、token建議別直接寫到春哥的context配置文件中,而是寫在您的生產容器的環境變量中,防止被竊取。
另外,在本方案中生成簽名的參數您能夠酌情增減並調換順序,替換籤名所採用的算法,或者根據您的實際須要「個性化」一下您的加密算法,以期達到更好的安全效果。
Last but not the least,在密鑰以及token交換的階段,請採起您所承認的安全有效的方式進行,譬如面對面,微信,qq,微薄私信,電話,短信,郵件(能夠參考本人以前寫過的一篇文章:http://lixuanbin.iteye.com/blog/1544344)
7、參考資料
【Java加密與解密的藝術】——做者:樑棟,出版日期:2010年12月,ISBN:978-7-111-29762-8
http://stackoverflow.com/questions/6729834/need-solution-for-wrong-iv-length-in-aes
http://baike.baidu.com/view/941329.htm?fr=aladdin
http://baike.baidu.com/subview/133041/5358738.htm?fr=aladdin