经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » Node.js » 查看文章
nodejs中各种加密算法的实现详解
来源:jb51  时间:2019/7/11 13:42:35  对本文有异议

前言

在大前端的趋势下,前端er也要懂点数据加密相关的知识才行,加密算法的实现和原理我们可以不用深究,毕竟加密是一门高深的学科,但是基本的加密方式和编码还是要掌握的,毕竟没吃过猪肉,猪跑还是见过的嘛。

我对常见的几种加密和签名的算法做个归纳,同时附上 nodejs 的编码实现。

加密算法

为了保证数据的安全性和防篡改,很多数据在传输中都进行了加密。举个场景的栗子,最近很多网站都升级到 https 协议, https 协议就是使用了非对称加密和hash签名,还有 github 使用的 ssh ,也是非对称加密。还有大部分登录时密码采用的 MD5 加密等等。

加密可分为三大类,对称加密和非对称加密,还有摘要算法,我们一一展开。

对称加密

引用百科的描述:

采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。

对称加密很好理解,就好比我把我家的钥匙给你,你要来我家,直接用这把钥匙开门就行。

对称加密目前主流的有 AES 和 DES , AES 是新一代的标准,速度快,安全级别更高。

AES

AES的加密模式有五种:CBC、ECB、CTR、OCF、CFB

  1. ECB:电子密本方式,需要一个密钥即可,特点是简单,利于并行计算。
  2. CBC:密文分组链接方式,除了需要一个密钥之外,还需要一个向量,向量的作用也是用于数据的加密,所以这个的安全性要好于 ECB
  3. CTR、OCF、CFB:具体算法的实现方式不一样,优缺点也各不相同,而这几个都同 CBC 一样,都需要密钥和向量。

AES 有三种长度 128位、192位、256位,这三种的区别,主要来自于密钥的长度,16字节密钥=128位,24字节密钥=192位,32字节密钥=256位。如下表格:

长度 密钥长度 向量长度
128位 16 16
192位 24 16
256位 32 16

DES

加密默认与 AES 相同,也有五种模式,除了 ECB 只需要密钥,其他模式需要密钥和向量。

与 AES 不同的是, DES 的密钥长度只有8字节,向量也是8字节。

编码实现

在 nodejs 中的实现

  1. /**
  2. * @description
  3. * 对称加密
  4. * @param {*} data 加密数据
  5. * @param {*} algorithm 加密算法
  6. * @param {*} key 密钥
  7. * @param {*} iv 向量
  8. * @returns
  9. */
  10. function cipherivEncrypt(data, algorithm, key, iv) {
  11. const cipheriv = crypto.createCipheriv(algorithm, key, iv)
  12. let encrypted = cipheriv.update(data, 'utf8', 'hex');
  13. encrypted += cipheriv.final('hex');
  14. return encrypted
  15. }
  16.  
  17. /**
  18. * @description
  19. * 对称解密
  20. * @param {*} data 解密数据
  21. * @param {*} algorithm 解密算法
  22. * @param {*} key 密钥
  23. * @param {*} iv 向量
  24. * @returns
  25. */
  26. function cipherivDecrypt(data, algorithm, key, iv) {
  27. const decipher = crypto.createDecipheriv(algorithm, key, iv);
  28. let decrypted = decipher.update(data, 'hex', 'utf8');
  29. decrypted += decipher.final('utf8');
  30. return decrypted
  31. }

使用官方提供 crypto 库来实现加解密,上面的代码中加密后输出的是 16 进制的字符串,大家可以根据具体情况换成其他格式的数据。

调用方式如下

  1. // AES对称加解密
  2. const str = 'xiaoliye';
  3. const key = 'aaaaaaaaaaaaaaaaaaaaaaaa'; // 24
  4. const iv = 'aaaaaaaaaaaaaaaaaaaaaaaa'; // 24
  5. const cipherAesText = cipherivEncrypt(str, 'aes-192-cfb', key,iv)
  6. const resultText = cipherivDecrypt(cipherAesText, 'aes-192-cfb', key,iv)
  7. console.log(resultText === str) // true
  1. // DES对称加解密
  2. const str = 'xiaoliye';
  3. const key = 'aaaaaaaa'; //8
  4. const iv = 'aaaaaaaa'; //8
  5. const cipherAesText = cipherivEncrypt(str, 'des-cfb', key,iv)
  6. const resultText = cipherivDecrypt(cipherAesText, 'des-cfb', key,iv)
  7. console.log(resultText === str) / true

非对称加密

非对称加密,有两把钥匙,公钥和私钥,如下图:

公钥是可以公开对外,私钥就是自个的,不可泄露。因为有两个密钥,非对称加密这个名字就是这么由来的。

发送方用接收方公开对外的公钥进行加密,接收方收到数据后,用私钥进行解密,业务处理完后,用私钥给需要回传的数据加密,收到数据的一方在用公钥解密。

这个过程就是非对称加解密,简单理解就是公钥加密的数据,用私钥解密;私钥加密的数据,用公钥解密。

非对称加密与对称加密相比,安全性要高很多。对于对称加密,密钥钥匙被某一方不小心泄露了,那秘文就有可能被破解和篡改。而非对称加密,公钥随意流通,只要颁发密钥的一方好好把私钥保管好,安全性是妥妥的。

编码实现

我们来看 node 中的编码实现,还是使用官方提供的 crypto 库

  • 加解密函数接收的数据是 Buffer 类型,(关于 Buffer 的介绍,不了解的朋友可以看下这篇二进制与Buffer),所以需要约定好接收和输出的数据类型。
  • 参数 padding 是填充方式,有这么几种 crypto.constants.RSA_NO_PADDING, crypto.constants.RSA_PKCS1_PADDING, crypto.constants.RSA_PKCS1_OAEP_PADDING,因为没有深入研究过,就不展开啦,不过有一点,加密和解密的填充方式必须是要一致的。
  1. const constants = require('constants')
  2. const crypto = require('crypto')
  3.  
  4. /**
  5. * @description
  6. * 公钥加密数据
  7. * @param {*} data 待加密数据
  8. * @param {*} publicKey 公钥
  9. * @param {*} inputEncoding 加密数据类型
  10. * @param {*} outputEncoding 输出的数据类型
  11. * @param {*} padding 填充方式
  12. * @returns
  13. */
  14. function publicEncrypt(data, publicKey, inputEncoding, outputEncoding, padding) {
  15. const encryptText = crypto.publicEncrypt({
  16. key: publicKey,
  17. padding: padding || constants.RSA_PKCS1_PADDING
  18. }, Buffer.from(data, inputEncoding));
  19.  
  20. return encryptText.toString(outputEncoding);
  21. }
  22.  
  23. /**
  24. * @description
  25. * 公钥解密数据
  26. * @param {*} data 待解密数据
  27. * @param {*} publicKey 公钥
  28. * @param {*} inputEncoding 解密数据类型
  29. * @param {*} outputEncoding 输出的数据类型
  30. * @param {*} padding 填充方式
  31. * @returns
  32. */
  33. function publicDecrypt(data, publicKey, inputEncoding, outputEncoding, padding) {
  34. let decryptText = '';
  35. const decryptText = crypto.publicDecrypt({
  36. key: publicKey,
  37. padding: padding || constants.RSA_PKCS1_PADDING
  38. }, Buffer.from(data, inputEncoding));
  39.  
  40. return decryptText.toString(outputEncoding);
  41. }
  42.  
  43. /**
  44. * @description
  45. * 私钥加密数据
  46. * @param {*} data 待加密数据
  47. * @param {*} privateKey 私钥
  48. * @param {*} inputEncoding 加密数据类型
  49. * @param {*} outputEncoding 输出的数据类型
  50. * @param {*} padding 填充方式
  51. * @returns
  52. */
  53. function privateEncrypt(data, privateKey, inputEncoding, outputEncoding, padding) {
  54. const encryptText = crypto.privateEncrypt({
  55. key: privateKey,
  56. padding: padding || constants.RSA_PKCS1_PADDING
  57. }, Buffer.from(data, inputEncoding));
  58.  
  59. return encryptText.toString(outputEncoding);
  60. }
  61.  
  62. /**
  63. * @description
  64. * 私钥解密数据
  65. * @param {*} data 待解密数据
  66. * @param {*} privateKey 私钥
  67. * @param {*} inputEncoding 解密数据类型
  68. * @param {*} outputEncoding 输出的数据类型
  69. * @param {*} padding 填充方式
  70. * @returns
  71. */
  72. function privateDecrypt(data, privateKey, inputEncoding, outputEncoding, padding) {
  73. const decryptText = crypto.privateDecrypt({
  74. key: privateKey,
  75. padding: padding || constants.RSA_PKCS1_PADDING
  76. }, Buffer.from(data, inputEncoding));
  77. return decryptText.toString(outputEncoding);
  78. }

有四个函数,分别是公钥的加解密和私钥的加解密,我们看下如何使用,示例中是我自己生成的密钥对,大家可以自行替换

  1. const rsaPublicKey = `-----BEGIN PUBLIC KEY-----
  2. MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCncWDMXEToSxtGQCmWY2ywl5CQ
  3. tb81PXYZch4v5M8MNUZPpcmf+VDXQbuWqqTqV/tY7rLviu/BAkFbX9NiFCapF5lP
  4. siVwSGWJQwq0S/++RCwB6yFVEzOKL25jANRBVNwmSOzojveCStYPcEs5Q829ld68
  5. 9TzluDDqUS69dTHGkQIDAQAB
  6. -----END PUBLIC KEY-----`
  7.  
  8. const rsaPrivateKey = `-----BEGIN PRIVATE KEY-----
  9. MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKdxYMxcROhLG0ZA
  10. KZZjbLCXkJC1vzU9dhlyHi/kzww1Rk+lyZ/5UNdBu5aqpOpX+1jusu+K78ECQVtf
  11. 02IUJqkXmU+yJXBIZYlDCrRL/75ELAHrIVUTM4ovbmMA1EFU3CZI7OiO94JK1g9w
  12. SzlDzb2V3rz1POW4MOpRLr11McaRAgMBAAECgYEAhNa8/cQh4sxbKgOTOr1MKFlG
  13. Fpgpxroo7I0Nh9+Vp1DIpD2Z1PF9ghijEyf0R/pe7LIKgWIPTWdVpIFEeSYVeH43
  14. FLr3zwR9oXzwG7RQTSN4d/Xcvg+24ZxCrvDfn7qDIlXh0jOS0wCvna1or7xgPcOu
  15. XG8J3BNbBdUixM0lk0ECQQDR4SCelWn0BY21jsFobX+pGqKOsj+tuvU4Cz47Gmev
  16. qvq2suYXwLemkP7EqRu8iNso/IzvrdsuJDG76dzwC4D5AkEAzDz2cDrKOVmqYw7s
  17. luOQFHl1TzmY7Umpd9YbZ5iXn0eCjIn1/e1risRF5+IeSpB84OVltUzj4cVDCbFd
  18. 9S1wWQJAIeKcFp5+9cPzxi1fMpIDO3Uua6WBvHXj44GFMZuow+byBY9KsOkPfZgJ
  19. Wg0Hil/6KlrkEkpaic+ULAetASCKWQJAdMh/Gdlj/LsaxJ2qBvWEU1DIFU8X9Mbk
  20. ElPpQ6lrOXaIXZgdgt8ZWTW1y0vuijBoV6iUKcEXpOdI1+gFk8YxsQJBAJsGJClf
  21. E1mE6CZgegM82428g4osZznVXBO/QtrQsA78S1xo8bo4qwVm0jQBcto65gwlfeeB
  22. Xm7MiIvNVBqzTVs=
  23. -----END PRIVATE KEY-----
  24. `
  25.  
  26. const str = 'xiaoliye'
  27. const cipherText = publicEncrypt(str, rsaPublicKey, 'utf8', 'hex') // 公钥加密
  28. const decryptText = privateDecrypt(cipherText, rsaPrivateKey, 'hex', 'utf8') // 私钥解密
  29. console.log(str === decryptText) // true
  30.  
  31. const cipherTextPrivate = privateEncrypt(str,rsaPrivateKey,'utf8', 'hex') // 私钥加密
  32. const decryptTextPublic = publicDecrypt(cipherTextPrivate,rsaPublicKey, 'hex', 'utf8') // 公钥解密
  33. console.log(str === decryptTextPublic) // true

密钥生成方式

网上有很多工具可以一键生成配对的公钥和私钥,淘宝、微信都有提供相关工具,或者使用 OpenSSL 生成也可以。

摘要算法(HASH)

把任意长度的输入,根据算法生成一串固定长度的伪随机数,这一算法就是摘要算法,它有这么几个特点

  1. 不需要密钥,加密出来的数据无法被解密,具有不可逆性。
  2. 生成的摘要长度是固定的,与输入无关。
  3. 相同的输入,使用相同的实现,生成的摘要一定相同;不同的输入,生成的摘要是大相径庭的,即,不会发生碰撞。

根据这些特点,摘要算法通常用于生成签名,用来验证数据的完整性。

还有用户密码的存储,如今密码的存储主流的方式,就是使用摘要算法生成唯一的标识,为了保证安全性,通常在生成摘要后再加上一串随机数(加盐salt),在来hash一次。

目前主流的实现有 MD5 和 SHA-2 , MD5 生成的摘要是 32 字节, sha256 生成的摘要是 64 字节。

编码实现

仍然是使用官方提供的 crypto 库

  1. /**
  2. * @description
  3. * md5
  4. * @param {*} data
  5. * @returns
  6. */
  7. function md5(data){
  8. const hash = crypto.createHash('md5');
  9. return hash.update(data).digest('hex');
  10. }
  11.  
  12. /**
  13. * @description
  14. * sha256
  15. * @param {*} data
  16. * @returns
  17. */
  18. function sha256(data){
  19. const hash = crypto.createHash('sha256');
  20. return hash.update(data).digest('hex');
  21. }
  22.  
  23. console.log(md5('asdf')) // 912ec803b2ce49e4a541068d495ab570
  24. console.log(sha256('asdf')) // f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b

小结

涉及加密的活一般是后台开发干的,但前端靓仔懂点加密,会让自己酷酷的~

小伙伴们还有遇到啥其他加密的方式,欢迎一起交流啊~

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对w3xue的支持。

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号