在日常的工作中,我们对外提供的接口或调用三方的接口往往有一步生成签名或验签的步骤,这个步骤主要是验证调用方是
不是合法的以及内容是否被修改。比如:对于某些网上公开下载的软件,视频,尤其是镜像文件。如果被修改了可能会导致用不了
或者其他的问题,发布者镜像MD5算法计算一组数值。让下载的用户进行MD5数值对比,也就是MD5校验啦。由于MD5加密不可逆算,
如果数值一样,那就表示文件没有被修改的。反之,则被修改了。
接下来通过文字介绍、代码、运行结果的方式给大家介绍RSA、MD5生成签名和验签;
一、MD5签名与验签
1.MD5介绍
MD5全名Message-Digest Algorithm 5(信息-摘要算法)是一种不可逆的加密算法。
MD5算法具有以下特点:
1、压缩性:任意长度的数据,算出的MD5值长度都是固定的。2、容易计算:从原数据计算出MD5值很容易。
3、抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。
4、强抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。
MD5生成签名和验签需要MD5 key,这个key值就是一段字符串没有任何限制比如:123456ADSEF
2.签名与验签流程
首先参数放入一个字符串数组signFields,把参数和值放入一个对象或map中,使用JSONObject把这个对象转化成json对象。
然后构建签名原文,在构建签名原文时,我们需把参数按照字典(比如a,b,c)顺序排序,具体排序方法直接调java的Arrays.sort方法。
然后按照key=value的方式把所有参数和值拼接成字符串,多个参数直接以“&”符号隔开,再然后把MD5 key拼接在该签名原文的最后。
最后使用MD5Encrypt.getMessageDigest(signSrc)生成签名。
验签很简单,验签方按照上面的签名流程生成的签名与传过来的签名作对比如果相等就验签成功,否则验签失败。
3.具体代码如下:
需验签的参数map:
Map<String ,Object> map=new HashMap<String,Object>(); map.put("name", "小明"); map.put("age", 12); map.put("sex", "男"); map.put("school", "xxx中学"); map.put("address", "xxx小区");MD5生成签名字符串:
/** * MD5生成签名字符串 * * @param map * 需签名参数 * @param key * MD5key * @return */ public static String MD5sign(Map<String, Object> map, String key) { String genSign = ""; try { String[] signFields = new String[5]; signFields[0] = "name"; signFields[1] = "age"; signFields[2] = "sex"; signFields[3] = "school"; signFields[4] = "address"; JSONObject param = (JSONObject) JSONObject.toJSON(map); // 生成签名原文 String signSrc = orgSignSrc(signFields, param); // MD5的方式签名 signSrc += "&KEY=" + key; genSign = MD5Encrypt.getMessageDigest(signSrc); } catch (Exception e) { e.printStackTrace(); } return genSign; }构建签名原文:
/** * 构建签名原文 * * @param signFilds 参数列表 * @param param 参数与值的jsonbject * @return */ private static String orgSignSrc(String[] signFields, JSONObject param) { if (signFields != null) { Arrays.sort(signFields); // 对key按照 字典顺序排序 } StringBuffer signSrc = new StringBuffer(""); int i = 0; for (String field : signFields) { signSrc.append(field); signSrc.append("="); signSrc.append((StringUtil.isEmpty(param.getString(field)) ? "" : param.getString(field))); // 最后一个元素后面不加& if (i < (signFields.length - 1)) { signSrc.append("&"); } i++; } return signSrc.toString(); }MD5验证签名:
/** * MD5验证签名 * @param map * @param key * @param sign * @return */ public static void vlidateMD5sign(Map<String ,Object> map,String key,String sign) { String vsign=MD5sign(map, key); System.out.println("MD5验证签名生成的签名:"+vsign); System.out.println("MD5验证签名生成的签名与原签名是否一致:sign=vsign true?false:"+(vsign.equals(sign))); }main方法:
public static void main(String[] args) { Map<String ,Object> map=new HashMap<String,Object>(); map.put("name", "小明"); map.put("age", 12); map.put("sex", "男"); map.put("school", "xxx中学"); map.put("address", "xxx小区"); /***MD5签名与验签**/ String key="123456ADSEF"; String sign= MD5sign(map,key); System.out.println("生成的MD5签名:"+sign); vlidateMD5sign(map, key, sign) ; }执行结果:
生成的MD5签名:A82ED0D0E0155D3926E0A6B6B3EE60C4 MD5验证签名生成的签名:A82ED0D0E0155D3926E0A6B6B3EE60C4 MD5验证签名生成的签名与原签名是否一致:sign=vsign true?false:true二、RSA签名与验签
1.RSA介绍
RSA是目前最有影响力的公钥加密算法,该算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对
其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥,即公钥,而两个大素数组合成私钥。公钥是可发布的供任何人使用,
私钥则为自己所有,供解密之用。解密者拥有私钥,并且将由私钥计算生成的公钥发布给加密者。加密都使用公钥进行加密,并将密文发送
到解密者,解密者用私钥解密将密文解码为明文。
以甲要把信息发给乙为例,首先确定角色:甲为加密者,乙为解密者。首先由乙随机确定一个KEY,称之为密匙,将这个KEY始终保
存在机器B中而不发出来;然后,由这个 KEY计算出另一个KEY,称之为公匙。这个公钥的特性是几乎不可能通过它自身计算出生成它的私钥。
接下来通过网络把这个公钥传给甲,甲收到公钥后,利用公钥对信息加密,并把密文通过网络发送到乙,最后乙利用已知的私钥,就对密文进
行解码了。以上就是RSA算法的工作流程。
2.生成签名与验签流程
生成签名方:首先对参数放入一个字符串数组signFields,把参数和值放入一个对象或map中,使用JSONObject把这个对象转化成json对象。
然后构建签名原文,在构建签名原文时,我们需把参数按照字典(比如a,b,c)顺序排序,具体排序方法直接调java的Arrays.sort方法。 然后使用RSA
的私钥对签名原文进行签名。
验签方:和生产签名方一样先生成签名原文,然后使用RSA的公钥、生成签名方传入的签名及签名原文对生成签名方传入的签名进行验证,验
证结果为true说明验证成功,否则为未通过。
3.具体的代码实现
注:生成公钥和私钥可以使用RSA的相关工具也可以使用在线的web工具,网上工具很多的,一搜就出来了。我是用的是:http://web.chacuo.net/netrsakeypair这个在线工具。我采用的是密钥长度采用1024。
需验签的参数map:
Map<String ,Object> map=new HashMap<String,Object>(); map.put("name", "小明"); map.put("age", 12); map.put("sex", "男"); map.put("school", "xxx中学"); map.put("address", "xxx小区");RSA公钥和私玥:
String prikey="MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAOq30rck7L3FshHVYWJK59sTToGMAn7WfYdrFN60AmPPyiMcIFXe3ZAxf7SWNbaQOPUz/xYr+oAXUBK17bykS/E2+Xa74wdN2VNbc7cZIggAjP9tGN0qhYTclbtC3pchcU8TVccrlVUN2lzJDLBHhPBDBFXzsQx9Vwtm2qjf2GcrAgMBAAECgYEAsHnz4aXOpkTNRSFVbiz5tLsIbNjTS4CDs1ysvWFE5rzls45DNa0yk2bUKPhDfHdli99DbO02FDbzCo5lKE+zlEHaC/WTp6guEe7jj5dwMl3shBZmgITCTk1/MQ46gGRG4RRADbQT/Y7tENp/GF3y9oJyJ+LmHFvfdEjSuY1/QzECQQD6aKqYFO8wuhLhy1fTvjMwlzok0szT9wTp+l6E7Ct9+csvdwaYjJrGsr6kUv+6YUwieSJ41lVtGnRy1oXEQG2TAkEA7/V35kYG+FMwYq/DOrBNaomRQGJVAOLzGRoK2dkjAkpoUAfzk4TTQ0KdJJ3T6mzF/6IQY+1oFDD42kNKJklfCQJARiya0i/bsC4VKI3RuRcuRUm8E6G3oRcym1d8sYd10MH1/QFAKfQNU+23m1lfLR4jNe34iSCXpBGr3JrdtdfQXQJAXgWRkGHZ800tRU3XMlTIULlMd6zP38QNOsWwgMGK7SfYjZs//opp+Q3N4v4QfedXAZ4vy+fHAzpZF7SMBkpzeQJALlMaKKeqKvPr8abXSRjW8u6s8tHaHX6CRV/1fGDX1bkUByqdFMO5CqIHn7isK2dHXI42bJVz63/d2Aax3lTbkA=="; String pubkey="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDqt9K3JOy9xbIR1WFiSufbE06BjAJ+1n2HaxTetAJjz8ojHCBV3t2QMX+0ljW2kDj1M/8WK/qAF1ASte28pEvxNvl2u+MHTdlTW3O3GSIIAIz/bRjdKoWE3JW7Qt6XIXFPE1XHK5VVDdpcyQywR4TwQwRV87EMfVcLZtqo39hnKwIDAQAB";生成RSA签名字符串:
/** * RSA生成签名字符串 * * @param map * 需签名参数 * @param prikey * rsa私钥 * @return */ public static String RSAsign(Map<String, Object> map, String prikey) { String genSign = ""; try { String[] signFields = new String[5]; signFields[0] = "name"; signFields[1] = "age"; signFields[2] = "sex"; signFields[3] = "school"; signFields[4] = "address"; JSONObject param = (JSONObject) JSONObject.toJSON(map); // 生成签名原文 String src = orgSignSrc(signFields, param); genSign = RsaUtil.sign(src, prikey); } catch (Exception e) { e.printStackTrace(); } return genSign; }构建签名原文:
/** * 构建签名原文 * * @param signFilds 参数列表 * @param param 参数与值的jsonbject * @return */ private static String orgSignSrc(String[] signFields, JSONObject param) { if (signFields != null) { Arrays.sort(signFields); // 对key按照 字典顺序排序 } StringBuffer signSrc = new StringBuffer(""); int i = 0; for (String field : signFields) { signSrc.append(field); signSrc.append("="); signSrc.append((StringUtil.isEmpty(param.getString(field)) ? "" : param.getString(field))); // 最后一个元素后面不加& if (i < (signFields.length - 1)) { signSrc.append("&"); } i++; } return signSrc.toString(); }RSA验证签名:
/** * RSA验证签名 * @param map 参与验签的参数 * @param sign 签名者传入的签名 * @param publickey 公钥 * @return */ public static String vlidateRSAsign(Map<String, Object> map, String sign, String publickey) { String genSign = ""; try { String[] signFields = new String[5]; signFields[0] = "name"; signFields[1] = "age"; signFields[2] = "sex"; signFields[3] = "school"; signFields[4] = "address"; JSONObject param = (JSONObject) JSONObject.toJSON(map); // 生成签名原文 String signSrc = orgSignSrc(signFields, param); // 调用工具类验签 boolean bool = RsaUtil.verify(signSrc, sign, publickey); System.out.println("验证签名生成的签名与原签名是否一致: true?false:" + bool); } catch (Exception e) { e.printStackTrace(); } return genSign; }main方法:
public static void main(String[] args) { Map<String ,Object> map=new HashMap<String,Object>(); map.put("name", "小明"); map.put("age", 12); map.put("sex", "男"); map.put("school", "xxx中学"); map.put("address", "xxx小区"); /***RSA签名与验签**/ String prikey="MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAOq30rck7L3FshHVYWJK59sTToGMAn7WfYdrFN60AmPPyiMcIFXe3ZAxf7SWNbaQOPUz/xYr+oAXUBK17bykS/E2+Xa74wdN2VNbc7cZIggAjP9tGN0qhYTclbtC3pchcU8TVccrlVUN2lzJDLBHhPBDBFXzsQx9Vwtm2qjf2GcrAgMBAAECgYEAsHnz4aXOpkTNRSFVbiz5tLsIbNjTS4CDs1ysvWFE5rzls45DNa0yk2bUKPhDfHdli99DbO02FDbzCo5lKE+zlEHaC/WTp6guEe7jj5dwMl3shBZmgITCTk1/MQ46gGRG4RRADbQT/Y7tENp/GF3y9oJyJ+LmHFvfdEjSuY1/QzECQQD6aKqYFO8wuhLhy1fTvjMwlzok0szT9wTp+l6E7Ct9+csvdwaYjJrGsr6kUv+6YUwieSJ41lVtGnRy1oXEQG2TAkEA7/V35kYG+FMwYq/DOrBNaomRQGJVAOLzGRoK2dkjAkpoUAfzk4TTQ0KdJJ3T6mzF/6IQY+1oFDD42kNKJklfCQJARiya0i/bsC4VKI3RuRcuRUm8E6G3oRcym1d8sYd10MH1/QFAKfQNU+23m1lfLR4jNe34iSCXpBGr3JrdtdfQXQJAXgWRkGHZ800tRU3XMlTIULlMd6zP38QNOsWwgMGK7SfYjZs//opp+Q3N4v4QfedXAZ4vy+fHAzpZF7SMBkpzeQJALlMaKKeqKvPr8abXSRjW8u6s8tHaHX6CRV/1fGDX1bkUByqdFMO5CqIHn7isK2dHXI42bJVz63/d2Aax3lTbkA=="; String pubkey="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDqt9K3JOy9xbIR1WFiSufbE06BjAJ+1n2HaxTetAJjz8ojHCBV3t2QMX+0ljW2kDj1M/8WK/qAF1ASte28pEvxNvl2u+MHTdlTW3O3GSIIAIz/bRjdKoWE3JW7Qt6XIXFPE1XHK5VVDdpcyQywR4TwQwRV87EMfVcLZtqo39hnKwIDAQAB"; String rsaSign= RSAsign(map,prikey); System.out.println("生成的RSA签名:"+rsaSign); vlidateRSAsign(map, rsaSign, pubkey) ; }执行结果:
生成的RSA签名:6AFF1E6A6CE17516D56ED94999E24FC6169290E111E207C4D9EFA57DA04525D173032FE32B620D16335164226420D0EDEE5EE5F9C9B413DAF2B7F418AE4EA17E055D718B1C1CB188A9BBBE1C5CF559C0BD5CADF83468D62C29635EF7CDE6B6AF0D63137A8FDA3CB26996DFBA3C505EDC04A843224AD1BBCA34ACD80EF7C3C5CA 验证签名生成的签名与原签名是否一致: true?false:true以上就是MD5、RSA签名与验签的介绍。
注:关于本文使用的MD5Encrypt、RsaUtil类文件及相关的jar包,由于代码比较多,在这里就写上去了。我直接把demo源码上传到我的空间,大家可以免费下载。地址如下: