一、关键词
HTTP,HTTPS,AES,SHA-1,MD5,消息摘要,数字签名,数字加密,Java,Servlet,Bouncy Castle

二、名词解释
数字摘要:是将任意长度的消息变成固定长度的短消息,它类似于一个自变量是消息的函数,也就是Hash函数。数字摘要就是采用单项Hash函数将需要加密的明文“摘要”成一串固定长度(128位)的密文这一串密文又称为数字指纹,它有固定的长度,而且不同的明文摘要成密文,其结果总是不同的,而同样的明文其摘要必定一致。
AES:密码学中的高级加密标准(Advanced Encryption Standard,AES),又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。是一种对称加密算法。
SHA-1:安全哈希算法(Secure Hash Algorithm)主要适用于数字签名标准 (Digital Signature Standard DSS)里面定义的数字签名算法(Digital Signature Algorithm DSA)。 SHA1有如下特性:不可以从消息摘要中复原信息;两个不同的消息不会产生同样的消息摘要。
MD5:Message Digest Algorithm MD5(中文名为消息摘要算法第五版)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护。

三、项目背景
某合作公司需要通过互联网向我司传递一些用户数据,但是我所在项目组的外网服务器上并无部署https,只能基于http进行数据传输。为了保护双方共同的用户数据,必须对在互联网上传输的信息进行加密处理。

四、方案设计
这里涉及到两个问题,一是采用什么样的远程消息传递框架,二是如何对传输的数据进行加密。
本人平时开发所用的语言主要是Java,对于Jsp/Servlet还比较熟悉,结合去年参加过所在公司的微信公众号开发的经验,设计出了如下方案:
1.在客户端采用构造http post请求,把用户数据加密后放入request body中,并在http参数中放入调用方的签名;
2.服务端接收到请求,提取参数进行签名校验,通过后从request body中提取密文进行解密,然后进行后续处理,最终生成响应返回给客户端。
以下是具体处理的流程图:

基于HTTP在互联网传输敏感数据的消息摘要、签名与加密方案_第1张图片
在数据加密阶段,基于性能以及效率考虑,采用了Bouncy Castle提供的AES算法,而生成签名则采用了jdk提供的SHA-1,值得注意的是,基于安全考虑,消息密文的消息摘要也被列入到参与数字签名的参数之一。

五、代码实现
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 = "testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttest";  
                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; 
* @param encName * , the algorithm name will be used, dafault to "MD5";
* @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 srcList = new ArrayList(); 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; } /** * 验证签名:
* 1.根据appid获取该渠道的token;
* 2.根据appid、token、lol以及时间戳计算一次签名;
* 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 = "testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttest"; 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 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> iterator = paraMap.entrySet()  
                                        .iterator();  
                        while (iterator.hasNext()) {  
                                Map.Entry 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 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 = "commons-langcommons-lang2.5";  
                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 paraMap = new HashMap();  
                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配置:

   
        org.bouncycastle   
        bcprov-jdk16   
        1.46   
  
  
        commons-lang  
        commons-lang  
        2.5  
  
  
        org.apache.httpcomponents  
        httpclient  
        4.2.5  
  
  
        org.apache.httpcomponents  
        httpmime  
        4.2.5  
  

六、结语
在本方案设计实现过程中,消息传递的框架采用的是Java开发者所熟悉的Servlet技术,摘要、签名、加密所采用的算法,以及所依赖的第三方jar也是比较有口碑又大众化的货,对于有类似需要的开发者来说,本方案具有一定的参考意义。远程传递消息框架以及生成签名的环节,主要是模仿了微信公众平台的消息交互方式以及生成签名的思路,而有所创新的一小点是,把消息密文的MD5值也参与到了签名运算中,增加了被仿冒的难度,同时也便于服务方校验消息在传递过程中是否有被第三方所篡改。
基于简化工程配置的考虑,本示例项目中没有使用spring,您可以在您的生产项目中把本示例中的代码改造成春哥的单例业务bean。密钥、token建议别直接写到春哥的context配置文件中,而是写在您的生产容器的环境变量中,防止被窃取。
另外,在本方案中生成签名的参数您可以酌情增减并调换顺序,替换签名所采用的算法,或者根据您的实际需要“个性化”一下您的加密算法,以期达到更好的安全效果。
Last but not the least,在密钥以及token交换的阶段,请采取您所认可的安全有效的方式进行,譬如面对面,微信,qq,微薄私信,电话,短信,邮件(可以参考本人之前写过的一篇文章:http://lixuanbin.iteye.com/blog/1544344)

七、参考资料
【Java加密与解密的艺术】——作者:梁栋,出版日期:2010年12月,ISBN:978-7-111-29762-8
http://stackoverflow.com/questions/4243650/aes-encryption-decryption-with-bouncycastle-example-in-j2me
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
http://baike.baidu.com/view/1228622.htm?fr=aladdin
http://baike.baidu.com/view/7636.htm?fr=aladdin

海量it视频获取 sprping hadop
基于HTTP在互联网传输敏感数据的消息摘要、签名与加密方案_第2张图片