数字摘要
数字摘要也称为消息摘要,通过对一个消息或文本做单向Hash函数计算而生成。接收方收到消息后,采用同样的Hash重新计算,将新产生的摘要与原摘要进行比较,如果不一致则说明传输的数据被篡改。
数字摘要采用单向Hash函数,将需要计算的内容摘要成固定长度的串,这个串也称为数字指纹。这个串有固定的长度,不同的明文通过摘要生成的数字指纹是不一样的。同样的明文其摘要必定一致。
当然Hash函数进行哈希的时候有个概念叫哈希碰撞,意思是不同的原始值经过哈希计算后得到的结果相同。一个哈希函数的好坏是由发生哈希碰撞的概率决定的,可以认为,生成的摘要长度越长,hash函数也就越安全。数字摘要是单向哈希得到的,无法通过摘要信息逆向得出原始值。
数字摘要或消息摘要有一下特点:
(1) 无论输入的消息有多长,计算出来的消息摘要的长度是固定的,如应用MD5(Message-Digest Algorithm 5)算法得到的消息摘要是128位二级制串,使用SHA-1算法得到的摘要串有160bit,SHA256算法得到的摘要串有256bit。当然实际使用时一般需要将摘要进行16进制编码,分别得到32,40和64位16进制串。
(2) 相同的输入得到的摘要串一致,不同输入得到的摘要串必然不同。目前主流的摘要算法产生哈希碰撞的概率极小,如SHA1是10^48之1,可以忽略不计。
(3) 消息摘要不包含原文的完整信息,只能进行正向的信息摘要,而不能通过摘要逆向得到原文。
目前主流编程语言都已有MD5算法的实现,如Java的DigestUtils.md5Hex(content)就可以直接得到经过16进制编码的数字摘要。
加密算法
通常人们也把经由MD5,SHA1等算法生成数字摘要的过程叫做加密,但严格意义上这并不是加密。根据数字摘要第三个特点我们知道,数字摘要是不含原文完整信息的,不能逆向得到原始信息。数字摘要只能验证信息的完整性,即是否在传输过程中被篡改。而经过加密算法得到的密文是含有原文的完整信息的。加密算法分为两类,一类是对称加密,一类是非对称加密。对称加密有一个对称密钥,信息发送方通过该密钥对要发送的消息进行加密,接收方通过该密钥对接收到的密文进行解密。而非对称加密则有一个密钥对,含两个密钥,一个私钥一个公钥。
常用的对称加密算法有DES,3DES,AES。对称加密算法中只有一种密钥,并且是非公开的。信息发送方和信息接收方都要知道该秘钥,一旦秘钥在分发时被泄露,加密信息就不再安全。与非对称加密算法相比,对称加密算法加密速度快。
##非对称加密算法
非对称加密算法使用最广泛的是RSA加密。基于Java的RSA算法的使用:
先生成秘钥对,然后通过秘钥对得到对应的私钥和公钥,并将公钥和私钥进行base64编码,方便传输。
/**
* 生成密钥对,以<密钥名,密钥编码后的字符串值>方式存储
* @param filePath 生成密钥的路径
* @return Map<String,String>
*/
public static Map<String,String> generateKeyPair(String filePath){
try{
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(algorithm);
//密钥位数
keyPairGen.initialize(keySize);
//生成密钥对
KeyPair keyPair = keyPairGen.generateKeyPair();
//获得公钥
PublicKey publicKey = keyPair.getPublic();
//获得私钥
PrivateKey privateKey = keyPair.getPrivate();
//为方便传输,对公钥进行base64编码
String publicKeyString = getKeyString(publicKey);
//为方便传输,对私钥进行base64编码
String privateKeyString = getKeyString(privateKey);
File path = new File(filePath);
if (!path.exists()){
path.mkdir();
}
//将密钥对存储到密钥文件中
BufferedWriter pubbw = new BufferedWriter(new FileWriter(filePath+"/publicKey.keystore"));
BufferedWriter pribw = new BufferedWriter(new FileWriter(filePath+"/privateKey.keystore"));
pubbw.write(publicKeyString);
pribw.write(privateKeyString);
pubbw.flush();
pribw.flush();
pubbw.close();
pribw.close();
//将生成的密钥对返回
Map<String,String> map = new HashMap<String, String>();
map.put(PUBLIC_KEY,publicKeyString);
map.put(PRIVATE_KEY,privateKeyString);
return map;
} catch(Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 通过密钥得到密钥字符串,并进行Base64转码
* @param key 密钥对象
* @return String 经过Base64转码之后的字符串
*/
public static String getKeyString(Key key) {
//将key转为字节数组
byte[] keyBytes = key.getEncoded();
//对key字节数组进行Base64转码,得到key字符串
return Base64.encodeBase64String(keyBytes);
}
反过来,得到经base64编码后的密钥之后也可以将其转换为PrivateKey和PublicKey。先对字符串类型的密钥进行Base64解码,得到keyBytes。然后将公钥转换为X509EncodedJeySpec对象,然后通过KeyFactory生成PublicKey对象。而私钥需要先转换成PKCS8EncodedKeySpec对象,再通过KeyFactory生成PrivateKey对象。得到PrivateKey和PublicKey之后才能进行加密解密等操作。
RSA的密钥是成对出现的,公钥提供给对方使用,私钥自己保留,不能泄露出去。从通信机制看,加密之后的密文是需要传输出去的,显然只能通过接收方的私钥进行解密。所以加密时通过报文接收方提供的公钥进行加密,接收方收到报文之后用自己的私钥解密。而签名的目的是为了验证数据的完整性,因此是发送方用自己的私钥签名,接收方用发送方的公钥验签。
数字签名
数字签名是非对称加密技术与数字摘要技术的综合运用,指的是将通信内容的摘要信息使用发送者的私钥进行加密,然后将密文和原文一起传输给信息的接收者,接受者通过发送者的公钥解密被加密的摘要信息,然后使用与发送方相同的摘要算法,对接收到的内容采用相同的方式产生摘要信息,与解密的摘要串进行对比,如果相同,说明接收到的内容是完整的,否则是不完整的。常用的数字签名算法包括MD5withRSA、SHA1withRSA等。Java基于Signature的签名过程如下:
byte[] keyBytes = Base64.decodeBase64(privateKey);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initSign(privateK);
signature.update(src.getBytes(charSet));
byte[] result = signature.sign();
return Base64.encodeBase64String(result);
摘要认证和签名认证
前面说了摘要和签名,以此保证数据的完整性和准确性,保证通信内容不被篡改。具体就是请求方按一定规则对请求参数进行组织,并通过摘要算法,生成请求参数的摘要。接收方根据同样的参数组织规则得到摘要源串,并使用相同的摘要算法计算出相应的摘要。将生成的摘要与接收到的摘要进行对比,相同说明内容是完整和准确的,否则经过篡改,这个摘要验证的过程也叫摘要认证。
但是对于摘要算法来说,其安全性取决于key(或者说盐)的安全性。由于通信的客户端和服务端采用的是相同的key,一旦key泄露,则只要知道摘要算法就能伪造出合法的请求或者响应的摘要。
签名认证与摘要认证类似。但是相较于摘要认证,签名认证更加安全,主要原因就是,签名时加签使用的是私钥,而验签时使用的是对外公开的公钥。私钥有私钥持有者保管,不需要传输给第三方,安全性大大提高。但相较于摘要认证,签名认证使用的非对称加密算法将消耗更多的时间和硬件成本。
具体采用何种方式保证通信内容的安全性,应视实际情况来决定。
通常来说,内部系统之间的通信,可采用摘要认证。因为内部系统之间不需要经过互联网,而是在局域网之间通信,本身就具有较高的可信度。有些接口甚至完全是不需要身份验证的。
与第三方通信首先是经过https协议的。在https基础上,可采用不同的安全机制。比如微信支付相关接口,采用的是MD5或SHA256摘要认证。支付宝开放平台采用的是RSA签名认证。如果通信时参数中含有敏感字段如个人信息,则还需要对敏感字段进行加密,加密方式上文也介绍过,使用对称加密算法如3DES或AES,也可以使用RSA加密。