加密&解密、签名&验签,在区块链、钱包等领域应用甚广,背后的原理或细节是什么?这里通过图文并茂的故事,外加撸代码的方式,一起理清弄透这4个概念。
一、故事角色和人设
正式开始之前,先介绍一下本文的主人翁Bob。他有2把秘钥:公钥、私钥;生活中,他会遇到3种类型的人:朋友Alice、骗子Doug、可信任的公证人CA。Bob的公钥是公开的,所有人都可见,私钥仅Bob自己可见。
Bob有2把秘钥:公钥BobPub、私钥BobPri
Bob会遇到3类人:Alice、CA、Doug是每类的代表
二、对称加密&解密
故事角色和人设交代清楚后,主人翁Bob遇到了第一个棘手难题:平时通过一款聊天App与朋友Alice联系,某一天收到一份神秘邮件,里面明文写满了历史聊天内容。吓一大跳,还好没聊隐私的事情。惊吓之后,Bob想到了第一个办法:
棘手难题1:明文聊天,网络传输过程中被劫持,有窃听风险;
解决办法1:与Alice约定一个暗号串,对聊天消息加密后再发送;
→ 具体操作步骤如下:
Alice 与 Bob 两人事先约定一个共用密钥key;
Alice 将消息用key加密,将密文邮递给 Bob;
Bob 收到密文后,用key解密,看到明文;
→ 这个过程用到的其实是密码学中的对称加密,实现代码如下:
var crypto = require('crypto');
// Encrypt
function encrypt(str, key) {
var cipher = crypto.createCipher('aes192', key);
var enc = cipher.update(str, 'utf8', 'hex');
enc += cipher.final('hex');
return enc;
}
// Decrypt
function decrypt(str, key) {
var decipher = crypto.createDecipher('aes192', key);
var dec = decipher.update(str, 'hex', 'utf8');
dec += decipher.final('utf8');
return dec;
}
var msg = 'Hey Bob, how about lunch at Taco Bell.';
// Both Bob and Alice have the same key
var key = 'aceport';
// Alice
encrypted_msg = encrypt(msg, key);
console.log(encrypted_msg);
// Bob
decrypted_msg = decrypt(encrypted_msg, key);
console.log(decrypted_msg);
三、非对称加密&解密
Alice是个开朗的姑娘,朋友很多,很快把Bob发明的加密聊天传播给了其它朋友。同时,Alice又是个粗心的姑娘,与所有朋友聊天都用了同一个暗号串。美好的日子没过多久,Bob又收到一份神秘邮件,里面除了满写满了明文聊天内容之外,还多了一串👻👻👻。大惊之后,Bob找极客朋友请教,找到了克服暗号串误漏的方法:
棘手难题2:共用秘钥key极易泄漏,最终导致消息内容泄漏;
解决办法2:使用基于公钥+私钥的非对称加密算法;
→ 具体操作步骤如下:
Alice 将消息用Bob公钥BobPub加密,将密文邮递给 Bob;
Bob 收到密文后,用自己的私钥BobPri解密,看到明文;
→ 非对称加密算法,实现代码如下:
const ursa = require('ursa');
const fs = require('fs');
msg = "Hey Bob, how about lunch at Taco Bell.";
// Alice has Bob's public key
var BobPub = ursa.createPublicKey(fs.readFileSync('./key/BobPub'));
console.log('Encrypt with Bob Public Key');
encrypted_msg = BobPub.encrypt(msg, 'utf8', 'base64');
console.log('encrypted:', encrypted_msg, '\n');
// Bob has his private key
var BobPri = ursa.createPrivateKey(fs.readFileSync('./key/BobPri'));
decrypted_msg = BobPri.decrypt(encrypted_msg, 'base64', 'utf8');
console.log('decrypted:', decrypted_msg, '\n');
四、签名&验签
自从用了非对称加密消息,由于其它人没有Bob的私钥,无法解密,再没收到神秘邮件。为解被调戏之辱,Bob决定向神秘人回了一封38个👻表情符号的邮件。
对抗与反对抗的博弈总是在进行,静好舒畅一段时间后,Bob发现收到朋友Alice的消息,解密后全是38个👻表情符号。很明显,Alice发的消息,在传输过程中被篡改了。大惊之后,Bob再次找极客朋友请教,再次找到防篡改的方法:
棘手难题3:消息被篡改,完整性有问题,有篡改风险;
解决办法3:使用签名+验签算法;
→ 具体操作步骤如下,Bob和Alice分别做如下操作:
Bob做如下操作:
Bob对消息内容做hash,得到指纹Message Digest;
Bob用私钥对Message Digest签名,得到Signature;
Bob将签名Signature,附到消息尾部,一并发给Alice;
Alice收到消息后,做如下操作:
取出消息的正文,做hash得到Message Digest;
从消息底部取出Signature;
调用验证函数Verify(Message Digest, Signature)计算得到key;
将key与本地保存的Bob公钥进行比较:若key==BobPub,则验证成功;否则,验证失败,消息被篡改过,或消息来源不是Bob;
→ 签名和验签算法,实现代码如下:
const ursa = require('ursa');
const fs = require('fs');
msg = "IT’S A SECRET TO Alice.";
// 1. Bob
// Bob has his private key and Alice's public key
var BobPri = ursa.createPrivateKey(fs.readFileSync('./key/BobPri'));
var AlicePub = ursa.createPublicKey(fs.readFileSync('./key/AlicePub'));
console.log('1. Encrypt with Alice Public; Sign with Bob Private');
var encrypted_msg = AlicePub.encrypt(msg, 'utf8', 'base64');
var signiture = BobPri.hashAndSign('sha256', msg, 'utf8', 'base64');
console.log('encrypted: ', encrypted_msg);
console.log('signed: ', signiture, '\n');
// 2. Alice
// Alice has her private key and Bob's public key
var AlicePri = ursa.createPrivateKey(fs.readFileSync('./key/AlicePri'));
var BobPub = ursa.createPublicKey(fs.readFileSync('./key/BobPub'));
console.log('2. Decrypt with Alice Private; Verify with Bob Public');
var decrypted_msg = AlicePri.decrypt(encrypted_msg, 'base64', 'utf8');
if (msg !== decrypted_msg) {
throw new Error("invalid decrypt");
} else {
console.log('decrypted: ', decrypted_msg);
}
decrypted_msg = new Buffer(decrypted_msg).toString('base64');
if (!BobPub.hashAndVerify('sha256', decrypted_msg, signiture, 'base64')) {
throw new Error("invalid signature");
} else {
console.log('Bob Signature?', true, '\n');
}
五、数字证书
在一些列对抗中,神秘人Doug发现Bob是个硬茬,而Alice是只小白鼠。于是在一个月黑风高的夜晚,入侵了Alice的电脑,用自己的公钥替换了Bob的公钥文件。第二天,Alice打开电脑,收到Doug伪装成Bob发来的签过名的消息,聊天App调用本地的假Bob公钥验签,通过。从此,Doug可顺利冒充Bob了:
Doug用自己的私钥做成"数字签名",发消息给Alice,让她做转账等操作;
收到消息后,Alice用假的Bob公钥进行验签通过,从而被骗;
人终究比机器聪明智慧,Alice被骗几次后,从蛛丝马迹中觉察到了:自己电脑中的Bob公钥有问题,可能是假的。于是,她找Bob,一起找到了德高望重的公证人CA,给出来解决方法:
棘手难题4:用户电脑被入侵,公钥被替换,有冒充风险;
解决办法4:使用数字证书,防止公钥被篡改;
→ 具体操作步骤如下:
证书中心CA用自己的私钥,对Bob的公钥和一些相关信息一并签名,生成"数字证书Digital Certificate"给Bob;
Bob拿到Digital Certificate后:每次发送消息时,除了给消息附上签名,还附上数字证书;
Alice收到信息后:先用CA的公钥验证数字证书,进而取到Bob的真实公钥,最后就能验证Signature及消息;
六、拓展:Https协议
Bob、Alice、Doug、CA四种角色,几乎可以拓展到绝大多数的安全场景。比如Bob为服务方,Doug为钓鱼网站,Alice为普通用户,CA为SSL证书颁发中心,就可以拓展出Https协议的雏形:
服务端Bob,将域名xxxxxx.com(相当于Bob的公钥),拿去向SSL证书中心购买数字证书: crt private、crt public, 并将所购证书配置到官网的nginx服务;
客户端用户Alice访问网站xxxxxx.com时,服务端将数字证书附在报文一并返回。浏览器调用预置的SSL证书中心的根证书对数字证书验签,算出服务端的公钥。若算出的公钥,与浏览器当前访问域名不同,则给用户发出“当前访问网站不安全”的警告,如下图1~4步骤所示;
钓鱼网站由于没有xxxxxx.com的数字证书,所以无法伪装成服务端Bob;
Https协议非常复杂,这里不深入讲述,感兴趣的朋友可带着如下问题,深入研究:
数字证书,保存在哪里?
CA的公钥,与根证书是什么关系?保存在哪里?
证书颁发机构、网站nginx配置、浏览器,如何运作?
七、结论
阅读完本文后,您可以开始着手检查自己设计的系统是否存在窃听或劫持风险、篡改风险、冒充风险,相信您也能coding出相应的解决方案。
最后,重要事说三遍,请记住下面两点:
公钥加密,私钥解密;
私钥签名,公钥验签;
参考文献:
What is a Digital Signature? http://www.youdzone.com/signature.html
数字签名是什么?http://www.ruanyifeng.com/blog/2011/08/what_is_a_digital_signature.html
==============================
知识星球福利:
陈老师亲自打理,每天和你分享AI技术、投资、创业、产品、区块链。正好知识星球活动,时间有限,同学们抓紧。