信息安全课程作业,敲了整整4天才基本搞定,还有一小问题没解决,可以的话评论区留言感激不尽。

总体思路:

该系统后端使用python的tornado框架(专门实现聊天功能的框架,笔者也只学了一天),前端使用vue框架(其实原生html也可以,我是觉得vue更方便管理代码)

前端js(客户端)使用RSA加密,即务端公钥加密,加密对象为m|H(m),即得到PUb[m|H(m)];后端python使用客户端私钥解密m和H(m),后端再对m生成消息摘要,通过判断消息摘要和H(m)是否相同来判断消息是否被篡改。此时实现了保密性和完整性。

前端再用客户端私钥对m生成签名sig(m),后端使用客户端公钥验证签名合法性,此时保障了不可抵赖性。

严格来说,前端加密后的消息应为PUb{m|sig[H(m)]},但我发送的消息为PUb[m|H(m)] | sig(m),原因暂且按下不表,下面会解释。

系统难点

如果只用js加解密和验签,或者只用python加解密验签,会很容易。这很好理解,毕竟同一语言中使用同一库不会出现不兼容的额问题。所以在不同语言中,前端和后端需要找到相互兼容的库,前端加密后的信息,后端需要解密得出来;前端签名后端也得能验签!

然后笔者发现python的Crypto库有点儿东西,加解密和签名验签全都能实现,还支持很多种相关算法,宝藏!!同时,前端的jsencrypt库和jsrsasign库是笔者找了好久,发现能和python的Crypto库兼容的加解密库和签名库。本次系统使用cdn引入,位置如下。

//MD5算法库
<script src="https://cdn.bootcss.com/blueimp-md5/2.10.0/js/md5.min.js"></script>
//加密库(本次用RSA算法加密)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsencrypt/3.2.1/jsencrypt.min.js"></script>
//签名库
<script src="https://cdn.bootcdn.net/ajax/libs/jsrsasign/10.5.13/jsrsasign-all-min.min.js"></script>

python的库也有点儿多,其中PKCS1_v1_5 笔者还不是很清楚,欢迎读者在评论区帮忙补充嘻嘻

from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5
import Crypto.Signature.PKCS1_v1_5 as sign_PKCS1_v1_5  # 用于签名/验签
from Crypto.PublicKey import RSA
import base64
from Crypto.Hash import MD5

密钥生成

在线RSA密钥对生成工具 - UU在线工具 (uutool.cn)

生成的密钥格式如下,将其保存为pem格式文件以便程序读取,一共会生成4个pem:前后端的公私钥。

-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDmaZl9cf9Z6RhGeLL1fgI4HuRPoS3IfkQF81Vlxf6NLrII5XEIWDyLHUyAiKpbOWdeup6Ra8btwTfMO5Jqa3eG4wKNKWoFNboTBtutriq9QRdfj3BQJjNieIAYN9Mykfxqkqh9+lEvjcm5MULeZRPkT4TjroEDiftegm2AYOOyPwIDAQAB-----END PUBLIC KEY-----

前端加密和签名

初始化变量,其实不需要下面这么多,按自己需要来就行。

// 服务端的公钥
publicKey_server: "-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDmaZl9cf9Z6RhGeLL1fgI4HuRPoS3IfkQF81Vlxf6NLrII5XEIWDyLHUyAiKpbOWdeup6Ra8btwTfMO5Jqa3eG4wKNKWoFNboTBtutriq9QRdfj3BQJjNieIAYN9Mykfxqkqh9+lEvjcm5MULeZRPkT4TjroEDiftegm2AYOOyPwIDAQAB-----END PUBLIC KEY-----",
// 服务端私钥
privateKey_server: "-----BEGIN RSA PRIVATE KEY-----MIICXgIBAAKBgQDmaZl9cf9Z6RhGeLL1fgI4HuRPoS3IfkQF81Vlxf6NLrII5XEIWDyLHUyAiKpbOWdeup6Ra8btwTfMO5Jqa3eG4wKNKWoFNboTBtutriq9QRdfj3BQJjNieIAYN9Mykfxqkqh9+lEvjcm5MULeZRPkT4TjroEDiftegm2AYOOyPwIDAQABAoGAbVo9yf8R+Rp69msvR/qLVBY5Nh+hSm++mfJgG8Kpqli4jydRi2vRJBb+KVxzOYNXb2pzekHj8g/LCwdU2GPzn+7R5sqEegf1oL+om7XQM7Ny4dXvqWhBvaiA1I0Bsj9m2MevOGRH9AtQTa3hUlbYvmP6toi2Tg8ewXfmMTlHodUCQQDyccQ8YaphQkKdy/WTt7CQfEYHVjaGoURkYg0ZauXHqNc0sngHd5rzKMLtOUBvEjHuy1/k01Ra7q2VV2uAwvvrAkEA80udNsSrkYLKv9/qwDjrwx2ibJqE4o3d+g4ob/22OB4ZnJqgRrVhM6pXPbqTsSHlochQMvlBXA5R7nnUw3px/QJBAMcqanjoCp2nXy5eNTnKdwPa83RngJeMt7B3VCeDR4yDyXcC/dO0j9gdrjRPCf20xsxSyk4ixXOGC5dZn3jBtU0CQQCAj9lQopZiuvF2eNVso+d5YER/DRvhN8QvqaGWpEPQ3Z79EPxWwOvPSFj3Zos608WrOtWeSfZOtcZ3tOtILIDlAkEA3iA8eaJto/aRSzpS1Q/30G7d+gv85yWk1dQD1y6hRVB9BQkbVw3mrjkn21UEWXaspzSXkeTzMAuVzQiu4UQUbg==-----END RSA PRIVATE KEY-----",
//客户端公钥
publicKey_client: "-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOViauny7DuRhO5GgtMC1kOizaQrBGRv6NrbhuIS+YoxMuf1mma1dBOZ7gd5fEDAxW2/S6jYzVI4vi33w8Z9K//HTMXnyBmerzRKEPFrP55YijltoE6eIOYCo0iuuG00ZvIS/QgXonrIZ/P4Qn4Nip89uMMo9G6EdW5vswyowvwQIDAQAB-----END PUBLIC KEY-----",
// 客户端私钥
privateKey_client: "-----BEGIN RSA PRIVATE KEY-----MIICWwIBAAKBgQDOViauny7DuRhO5GgtMC1kOizaQrBGRv6NrbhuIS+YoxMuf1mma1dBOZ7gd5fEDAxW2/S6jYzVI4vi33w8Z9K//HTMXnyBmerzRKEPFrP55YijltoE6eIOYCo0iuuG00ZvIS/QgXonrIZ/P4Qn4Nip89uMMo9G6EdW5vswyowvwQIDAQABAoGAOGOt/aIOPzoaDRF58QOOHCqT8HAySXqEfcbAtQRHxDCpANeX8hW6yl4Lb+/vH4plYuWI2+TsXKFyzOVjyARdwVDaaSz9Nod5xfgxfshiXbkWQ8lF6jnx0KY/gTP3xowN9iMtWaZ6bapqZ8b9lU1wxx/sAaJMpU5JhzRI8XaNtDsCQQDPMIAKoGIgf10IcEnle51BPtw4DWyyWH6+38eZ9i2jZklwAOLNlVc8W/B1mqQTn50c6MKlZjkx4KCLSzavjf9HAkEA/vI2HC6S01tB7wsAJ2CJ7kNn4iioXtCRm9fATNQuOx/QX5yW0PVFWF/BMDfozDbKd0Roidjz1AxB4oG8EMGstwJAAWQZ9/hLsFwqi7v1Qw0paR6668VrTWc6sp1eAbKda9Nr+syGuUqfY1BatO9s2pTfwSnu5J1jFOqlKUo/+73AUQJAd34VCH53yOKD88NnLg2ceHVVcnX1/IKrTK0B78CfbozJwJaHRac/+lzfEneSAG1J1j7U9I8gMWoRU1XVTNFJ3wJAV3CWz7UQFfnrRsEX61Tb11G+tBBNWwC5WlUDR3Cji6tZPw4naUq6xS/4Jd4dTJmIrtEbPszepw4AzSZMPeMFSg==-----END RSA PRIVATE KEY-----",encrypt_client_public: "",
decrypt_client_public: "",
encrypt_client_private: "",
decrypt_client_private: "",
encrypt_server_public: "",
decrypt_server_public: "",
decrypt_server_private: "",this.encrypt_client_public = new JSEncrypt(),  // 实例化对象
this.decrypt_client_public = new JSEncrypt(),  // 实例化对象
this.encrypt_client_private = new JSEncrypt(),  // 实例化对象
this.decrypt_client_private = new JSEncrypt(), // 实例化对象this.encrypt_server_public = new JSEncrypt(),  // 实例化对象
this.decrypt_server_public = new JSEncrypt(),  // 实例化对象
this.encrypt_server_private = new JSEncrypt(),  // 实例化对象
this.decrypt_server_private = new JSEncrypt(), // 实例化对象
//客户端
this.encrypt_client_public.setPublicKey(this.publicKey_client),  // 设置加密公钥
this.decrypt_client_public.setPublicKey(this.publicKey_client),  // 设置解密公钥
this.encrypt_client_private.setPrivateKey(this.privateKey_client),  // 设置加密私钥
this.decrypt_client_private.setPrivateKey(this.privateKey_client)  // 设置解密私钥
//服务器
this.encrypt_server_public.setPublicKey(this.publicKey_server),  // 设置加密公钥
this.decrypt_server_public.setPublicKey(this.publicKey_server),  // 设置解密公钥
this.encrypt_server_private.setPrivateKey(this.privateKey_server),  // 设置加密私钥
this.decrypt_server_private.setPrivateKey(this.privateKey_server)  // 设置解密私钥       //客户端私钥签名函数
sign_c: function (plaintext) {signature1 = this.encrypt_client_private.sign(plaintext, CryptoJS.MD5, "md5");  //客户端私钥加签return signature1},
//客户端公钥验签函数(本系统中无需使用该函数)
verify_c: function (plaintext, signature) {this.verified = this.decrypt_client_public.verify(plaintext, signature, CryptoJS.MD5); //客户端公钥验签return this.verified},        

注意:笔者是用的vue框架,密钥的初始化在不同区域:一个在data函数中返回,一个在created周期中。

初始化完成后,就可以调用服务器公钥的加密函数了,即:

send: function () {
console.log("服务端公钥加密↓");
msg_md5 = md5(msg.value);
temp = msg.value + "|" + msg_md5
console.log("temp:", temp);
message1 = this.encrypt_server_public.encrypt(temp);
console.log("message1:", message1);
console.log("客户端私钥签名↓");
message2 = this.sign_c(msg.value)
console.log("message2:", message2);
ws.send(message1 + "|" + message2)//发送到后端,PUb(m|H(m))|sign(m)
this.$message({ //ElementUI的消息提示功能
message: '发送成功',type: 'success'});
msg.value = '' //将输入框中的值置空},

至此,前端实现了消息加密和签名。

后端解密和验签

不同于前端,后端的秘钥不是直接定义在程序代码里,而是通过读取pem文件拿到。ps:下面的秘钥也不需要完全用到,毕竟服务端怎么可以拿到客户端的私钥,写上去只是方便调试。


# 加载服务器公钥文件
with open('public_server.pem', 'r') as f:public_server_key = f.read()
rsakey1 = RSA.importKey(public_server_key)
public_server = Cipher_pkcs1_v1_5.new(rsakey1)# 加载服务器私钥文件
with open('private_server.pem', 'r') as f:private_server_key = f.read()
rsakey3 = RSA.importKey(private_server_key)
private_server = Cipher_pkcs1_v1_5.new(rsakey3)# 加载客户端公钥文件
with open('public_client.pem', 'r') as f:public_client_key = f.read()
rsakey2 = RSA.importKey(public_client_key)
public_client = rsakey2 //注意,这里没有调用Cipher_pkcs1_v1_5方法# 加载客户端私钥文件
with open('private_client.pem', 'r') as f:private_client_key = f.read()
rsakey4 = RSA.importKey(private_client_key)
private_client = Cipher_pkcs1_v1_5.new(rsakey4)

初始化好秘钥过后 ,就可以对前端拿到的消息解密了,解密很简单,一行代码就可以搞定,甚至不需要封装函数。其中的“|”就是字符串“|”,笔者用“|”把他们串联在一起了。这也导致一个bug,如果明文消息中自带字符串“|”,那么消息认证和签名验证都会失败。不过小问题。

//前端发送的消息格式为PUb[m|H(m)]|sig(m)
msg1 = message.split("|", 1)[0] //通过分割|得到PUb[m|H(m)],再用自己的私钥解密
msg1 = private_server.decrypt(base64.b64decode(msg1), None).decode()
print("解密msg1得到:",msg1)

此时再对msg1进行分割,得到m和H(m),后端通过生成H(m)与前端发来的H(m)对比就可以判断完整性了。

//参数依次为签名,原文,公钥
def to_verify(signature, plain_text, public_key):# 验签verifier = sign_PKCS1_v1_5.new(public_key)_rand_hash = MD5.new()_rand_hash.update(plain_text.encode())verify = verifier.verify(_rand_hash, signature)return verify  # true / falsemsg11 = msg1.split("|", 1)[0] //m
msg12 = msg1.split("|", 1)[1] //H(m)sign = message.split("|", 1)[1] //sig(m)部分
print("sign:",sign)
sign = base64.b64decode(sign)verify = to_verify(sign, msg11, public_client)
if(verify):print("签名安全")

至此,后端验证成功

最后的疑问

既然既能实现签名,也能实现加密,为什么该系统中不能向后端发送PUb{m|sig[H(m)]}呢?

因为签名得到的字符串很长!!!!长到服务端公钥无法加密!!!

console.log("客户端私钥签名↓");
message2 = this.sign_c(msg.value) //生成m的签名
temp = msg.value + "|" + message2
console.log("原文|签名:",temp);
console.log("服务端公钥加密↓");
message = this.encrypt_server_public.encrypt(temp);
console.log("公钥加密:",message);

其是也找到了解决办法,就是js使用encryptlong库加密,这个库可以加密超长字符,而且和encrypt库兼容,但是我找了半天没找到这个库的cdn地址,只有npm安装方法(encryptlong - npm (npmjs.com)),可是我是html静态文件啊,就算安装了html也不能引入本地js文件,最后也就不了了之了。但凡开发者上点心整个cdn地址就好了呜呜呜。

前后端跨语言RSA加解密和签名验证实现(js+python)相关推荐

  1. angular和JAVA实现aes、rsa加密解密,前后端交互,前端加解密和后端JAVA加解密实现

    今天实现了下AES和RSA加密解密,主要的功能是对前后端交互数据进行加密解密,为什么要用到两个算法呢,首先RSA默认的话加密长度是有限的100多个byte吧大约,并且需要公钥私钥,而AES加密没有限制 ...

  2. RSA加解密及RSA签名验证

    工作中用到了非对称加密:RSA加解密及签名验证,根据查到的信息及工作中的问题总结,现在整理如下: 1. 准备好公钥和私钥,使用openssl工具生成RSA公钥和私钥对 1)生成RSA私钥: genrs ...

  3. 前后端java+vue 实现rsa 加解密与摘要签名算法

    RSA有两个密钥,一个是公开的,称为公开密钥:一个是私密的,称为私密密钥. 特点: 公开密钥是对大众公开的,私密密钥是服务器私有的,两者不能互推得出. 用公开密钥对数据进行加密,私密密钥可解密:私密密 ...

  4. 与非java语言使用RSA加解密遇到的问题:algid parse error, not a sequence

    遇到的问题 在一个与Ruby语言对接的项目中,决定使用RSA算法来作为数据传输的加密与签名算法.但是,在使用Ruby生成后给我的私钥时,却发生了异常:IOException: algid parse ...

  5. openssl在多平台和多语言之间进行RSA加解密注意事项

    首先说一下平台和语言: 系统平台为CentOS6.3,RSA加解密时使用NOPADDING进行填充 1)使用C/C++调用系统自带的openssl 2)Android4.2模拟器,第三方openssl ...

  6. 区块链背后的信息安全(4)RSA加解密及签名算法的技术原理及其Go语言实现

    # RSA加解密及签名算法的技术原理及其Go语言实现 对称加密中,加密和解密使用相同的密钥,因此必须向解密者配送密钥,即密钥配送问题. 而非对称加密中,由于加密和解密分别使用公钥和私钥,而公钥是公开的 ...

  7. C语言实现简单的RSA加解密算法

    使用c语言实现了简单的RSA加解密算法. 实验内容: 1.输入两个素数,然后生成一个随机数,计算出随机数的逆元,然后保存这些信息: 2.选择加密,则输入明文,输出密文: 3.选择解密,则输入密钥,输出 ...

  8. C语言RSA实现对字符串加密,C语言实现RSA加解密算法

    http://www.open-open.com/code/view/1435718537888 2015.071. RSA说明 RSA公钥加密算法是1977年由Ron Rivest.Adi Sham ...

  9. RSA加解密算法原理

    本文译自http://www.muppetlabs.com/~breadbox/txt/rsa.html,作者Brian Raiter. This article is translated from ...

最新文章

  1. python实现matlab中的diff,Python实现matlab数据绘制
  2. 面试时写不出排序算法?看这篇就够了
  3. java 模拟时钟_java模拟时钟
  4. AI算法工程师之路 梯度下降百度总结 练习机器学习的网站
  5. React 2019年路线图发布!Hooks明年一季度上线
  6. BATZ,一份《Android架构开发手册》就够,已offer
  7. 65条最常用正则表达式
  8. BZOJ-1927-星际竞速-SDOI2010
  9. Boost:字符串替换的测试程序
  10. 男人必看的46条忠告
  11. JMetro版本5发布
  12. MFC编程入门之十三(对话框:属性页对话框及相关类的介绍)
  13. c读取txt文件_第93天:文件读写
  14. Delta-wave
  15. Vijos P1975 扫雷游戏【谜题】
  16. JDBC连接池JDBCTemplate
  17. 鼠标移入移出时定时器加速的原因_2020年值得购买的鼠标有哪些?
  18. 【数仓】大数据领域建模综述-《大数据之路》读书笔记
  19. springboot获取国家法定节假日
  20. Win10 LTSB/LTSC安装微软商店/Microsoft store

热门文章

  1. 【技法操作】UI界面设计,用PS绘制录音页面教程
  2. 高斯投影坐标计算例题_测量学高斯投影已知横坐标如何求在第几度带投影计算而得的?例如:...-y坐标的自然值怎么算-数学-莫囤料同学...
  3. arnold运动模糊nuke合成方法
  4. vscode编译、调试stm32F4系列mcu的程序
  5. Qtum量子链发布QIP-19支持隐私资产技术提案
  6. 【Monkey Run】Excel编程 VBA
  7. 企业级技术与大数据BI——节选自专著《Big Data Fundamentals: Concepts, Drivers Techniques》
  8. 向上沟通:你必须要注意的三个误区
  9. 010❤Anaconda的安装及使用方法
  10. gcc -Wall -pedantic -ansi(转载)