理清弄透:加密&解密、签名&验签

加密&解密、签名&验签,在区块链、钱包等领域应用甚广,背后的原理或细节是什么?这里通过图文并茂的故事,外加撸代码的方式,一起理清弄透这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约定一个暗号串,对聊天消息加密后再发送;

→ 具体操作步骤如下:

  1. Alice 与 Bob 两人事先约定一个共用密钥key

  2. Alice 将消息用key加密,将密文邮递给 Bob;

  3. Bob 收到密文后,用key解密,看到明文;

理清弄透:加密&解密、签名&验签

→ 这个过程用到的其实是密码学中的对称加密,实现代码如下:

var crypto = require('crypto');
// Encryptfunction encrypt(str, key) { var cipher = crypto.createCipher('aes192', key); var enc = cipher.update(str, 'utf8', 'hex'); enc += cipher.final('hex'); return enc;}
// Decryptfunction 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 keyvar key = 'aceport';
// Aliceencrypted_msg = encrypt(msg, key);console.log(encrypted_msg);
// Bobdecrypted_msg = decrypt(encrypted_msg, key);console.log(decrypted_msg);

三、非对称加密&解密

Alice是个开朗的姑娘,朋友很多,很快把Bob发明的加密聊天传播给了其它朋友。同时,Alice又是个粗心的姑娘,与所有朋友聊天都用了同一个暗号串。美好的日子没过多久,Bob又收到一份神秘邮件,里面除了满写满了明文聊天内容之外,还多了一串👻👻👻。大惊之后,Bob找极客朋友请教,找到了克服暗号串误漏的方法:

  • 棘手难题2共用秘钥key极易泄漏,最终导致消息内容泄漏;

  • 解决办法2:使用基于公钥+私钥的非对称加密算法;

→ 具体操作步骤如下:

  1. Alice 将消息用Bob公钥BobPub加密,将密文邮递给 Bob;

  2. Bob 收到密文后,用自己的私钥BobPri解密,看到明文;

理清弄透:加密&解密、签名&验签

→ 非对称加密算法,实现代码如下:

const ursa = require('ursa');const fs = require('fs');
msg = "Hey Bob, how about lunch at Taco Bell.";
// Alice has Bob's public keyvar 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 keyvar 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做如下操作:

  1. Bob对消息内容做hash,得到指纹Message Digest;

    理清弄透:加密&解密、签名&验签

  2. Bob用私钥对Message Digest签名,得到Signature;

    理清弄透:加密&解密、签名&验签

  3. Bob将签名Signature,附到消息尾部,一并发给Alice;

理清弄透:加密&解密、签名&验签

Alice收到消息后,做如下操作:

理清弄透:加密&解密、签名&验签

  1. 取出消息的正文,做hash得到Message Digest

  2. 从消息底部取出Signature

  3. 调用验证函数Verify(Message DigestSignature)计算得到key

  4. 将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 keyvar 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 keyvar 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出相应的解决方案。

最后,重要事说三遍,请记住下面两点:

  • 公钥加密私钥解密;

  • 私钥签名公钥验签;

参考文献:

  1. What is a Digital Signature? http://www.youdzone.com/signature.html

  2. 数字签名是什么?http://www.ruanyifeng.com/blog/2011/08/what_is_a_digital_signature.html

==============================

知识星球福利:

陈老师亲自打理,每天和你分享AI技术、投资、创业、产品、区块链。正好知识星球活动,时间有限,同学们抓紧。


理清弄透:加密&解密、签名&验签

Click to rate this post!
[Total: 0 Average: 0]

人已赞赏
名家说每日优选

比年轻人贵,没年轻人身体好,为什么要你?| HR眼中的「中年职场人」

2019-4-21 0:19:05

名家说每日优选

从炒币到专业投资人,Grace的完美转身!

2019-4-21 0:19:19

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
有新消息 消息中心
搜索