Flask框架实现的前端RSA加密与后端Python解密功能详解


Posted in Python onAugust 13, 2019

本文实例讲述了Flask框架实现的前端RSA加密与后端Python解密功能。分享给大家供大家参考,具体如下:

前言

在使用 Flask 开发用户登录API的时候,我之前都是明文传输 username 和 password。这种传输方式有一定的安全隐患,password 可能会在传输过程中被窃听而造成用户密码的泄漏。

那么我认为解决该问题的方法是这样的:在前端页面对数据进行加密,然后再发送到后端进行处理。

这一篇文章是前端用 RSA 的 publicKey 进行加密,然后后端用 Python 进行解密的示例。

工具列表

  • 后端:Python3
    • Flask
    • PyCrypto(PyCrytodome)
  • 前端
    • jsencrypt.js

后端使用Cryptodome库进行密钥的生成和解密,前端则使用jsencrypt.js库进行加密。

阅读提醒

本文主要是提供前端RSA加密后端Python解密代码示例,不会做太详细的说明,也不会有代码打包下载链接,原理与步骤请细读示例代码或查阅相关资料。

后端

下面首先说明Python后端所用到的工具。

PyCrypto和PyCrytodome

PyCrypto 可能是 Python 中密码学方面最有名的第三方软件包。可惜的是,它的开发工作于2012年就已停止。幸运的是,有一个该项目的分支取代了 PyCrypto ,那就是 PyCrytodome。

Linux下安装命令:

pip install pycryptodome

Windows下安装命令:

pip install pycryptodomex

PyCrytodome使用示例

安装好 pycryptodome 后,下面放上示例代码RSA_demo.py

#!/usr/bin/env python3
# coding=utf-8
# Author: yannanxiu
"""
create_rsa_key() - 创建RSA密钥
my_encrypt_and_decrypt() - 加密解密测试
"""
from Cryptodome.PublicKey import RSA
from Cryptodome.Cipher import PKCS1_OAEP, PKCS1_v1_5
def create_rsa_key(password="123456"):
  """
  创建RSA密钥
  步骤说明:
  1、从 Crypto.PublicKey 包中导入 RSA,创建一个密码
  2、生成 1024/2048 位的 RSA 密钥
  3、调用 RSA 密钥实例的 exportKey 方法,传入密码、使用的 PKCS 标准以及加密方案这三个参数。
  4、将私钥写入磁盘的文件。
  5、使用方法链调用 publickey 和 exportKey 方法生成公钥,写入磁盘上的文件。
  """
  key = RSA.generate(1024)
  encrypted_key = key.exportKey(passphrase=password, pkcs=8,
                 protection="scryptAndAES128-CBC")
  with open("my_private_rsa_key.bin", "wb") as f:
    f.write(encrypted_key)
  with open("my_rsa_public.pem", "wb") as f:
    f.write(key.publickey().exportKey())
def encrypt_and_decrypt_test(password="123456"):
  # 加载公钥
  recipient_key = RSA.import_key(
    open("my_rsa_public.pem").read()
  )
  cipher_rsa = PKCS1_v1_5.new(recipient_key)
  en_data = cipher_rsa.encrypt(b"123456")
  print(len(en_data), en_data)
  # 读取密钥
  private_key = RSA.import_key(
    open("my_private_rsa_key.bin").read(),
    passphrase=password
  )
  cipher_rsa = PKCS1_v1_5.new(private_key)
  data = cipher_rsa.decrypt(en_data, None)
  print(data)
if __name__ == '__main__':
  # create_rsa_key()
  encrypt_and_decrypt_test()

其中create_rsa_key()为创建密钥对,encrypt_and_decrypt_test()为加密解密的测试,用起来很简单对吧?

既然知道如何在Python端解密数据了,那么下面就是前端的代码:

前端

前端所用到的主要工具是jsencrypt.js

jsencrypt.js简介

jsencrypt.js是一个提供RSA加密、解密和密钥生成的JS库。其使用方式也非常简单。在其官网就有给出示例代码。

Flask工程示例

Python后端

新建一个Python脚本,取名为rsa_flask_demo.py,把下面代码复制过去。

#!/usr/bin/env python3
# coding=utf-8
# Author: yannanxiu
import os
from flask import Flask, render_template, request, current_app
from Cryptodome.PublicKey import RSA
from Cryptodome.Cipher import PKCS1_OAEP, PKCS1_v1_5
import base64
from urllib import parse
# 获取当前路径
curr_dir = os.path.dirname(os.path.realpath(__file__))
private_key_file = os.path.join(curr_dir, "my_private_rsa_key.bin")
public_key_file = os.path.join(curr_dir, "my_rsa_public.pem")
app = Flask(__name__)
def decrypt_data(inputdata, code="123456"):
  # URLDecode
  data = parse.unquote(inputdata)
  # base64decode
  data = base64.b64decode(data)
  private_key = RSA.import_key(
    open(curr_dir + "/my_private_rsa_key.bin").read(),
    passphrase=code
  )
  # 使用 PKCS1_v1_5,不要用 PKCS1_OAEP
  # 使用 PKCS1_OAEP 的话,前端 jsencrypt.js 加密的数据解密不了
  cipher_rsa = PKCS1_v1_5.new(private_key)
  # 当解密失败,会返回 sentinel
  sentinel = None
  ret = cipher_rsa.decrypt(data, sentinel)
  return ret
@app.route('/', methods=["GET", "POST"])
def rsa():
  public_key = None
  if "GET" == request.method:
    with open(public_key_file) as file:
      public_key = file.read()
  elif "POST" == request.method:
    username = request.values.get("username")
    password = request.values.get("passwd")
    current_app.logger.debug("username:" + username + "\n" + "password:" + password)
    # decrypt
    username_ret = decrypt_data(username)
    password_ret = decrypt_data(password)
    if username_ret and password_ret:
      current_app.logger.debug(username_ret.decode() + " " + password_ret.decode())
  return render_template("rsa_view.html", public_key=public_key)
@app.route('/js_rsa_test', methods=["GET", "POST"])
def js_rsa_test():
  return render_template("js_rsa_test.html")
if __name__ == '__main__':
  app.run(debug=True)

rsa_flask_demo.py与前面的RSA_demo.py脚本放在一起,再用RSA_demo.py生成一组密钥对。或者把前面生成密钥对文件放在同一个目录下也可以。

前端代码

在同一目录下新建一个templates文件夹,用来存放Flask的前端模板。

在templates文件夹新建rsa_view.html,并拷贝下面代码过去,该HTML文件与Flask中的rsa()相对应。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  <title></title>
  <script src="http://code.jquery.com/jquery-1.8.3.min.js"></script>
  <script src="http://passport.cnblogs.com/scripts/jsencrypt.min.js"></script>
  <script type="text/javascript">
    // 使用jsencrypt类库加密js方法,
    function encryptRequest(reqUrl, data, publicKey) {
      var encrypt = new JSEncrypt();
      encrypt.setPublicKey(publicKey);
      // ajax请求发送的数据对象
      var sendData = new Object();
      // 将data数组赋给ajax对象
      for (var key in data) {
        sendData[key] = encrypt.encrypt(data[key]);
      }
      console.info(sendData);
      $.ajax({
        url: reqUrl,
        type: 'post',
        data: sendData,
        dataType: 'json',
        //contentType: 'application/json; charset=utf-8',
        success: function (data) {
          console.info(data);
        },
        error: function (xhr) {
          //console.error('出错了');
        }
      });
    }
    // Call this code when the page is done loading.
    $(function () {
      $('#testme').click(function () {
        var data = [];
        data['username'] = $('#username').val();
        data['passwd'] = $('#passwd').val();
        var pkey = $('#pubkey').val();
        encryptRequest('/', data, pkey);
      });
    });
  </script>
</head>
<body>
<form id="form1" runat="server">
  <div>
    <label for="pubkey">Public Key</label><br/>
    <textarea id="pubkey" rows="15" cols="65">
        {{ public_key }}
      </textarea><br/>
    <label for="input">Text to encrypt:</label><br/>
    name:<input id="username" name="username" type="text" value="user"></input><br/>
    password:<input id="passwd" name="passwd" type="password" value="123"></input><br/>
    <input id="testme" type="button" value="submit"/><br/>
  </div>
</form>
</body>
</html>

运行rsa_flask_demo.py,访问http://127.0.0.1:5000/,即可看到下面页面。直接点击submit按钮即可。

Flask框架实现的前端RSA加密与后端Python解密功能详解

当后台页面打印:

--------------------------------------------------------------------------------
DEBUG in rsa_flask_demo [F:/Flask/RSA_Flask/rsa_flask_demo.py:57]:
user 123
--------------------------------------------------------------------------------

即说明解密成功!

其他

rsa_flask_demo.py中的js_rsa_test()为JS脚本测试页面,其对应的HTML代码如下:

<!doctype html>
<html>
 <head>
  <title>JavaScript RSA Encryption</title>
  <script src="http://code.jquery.com/jquery-1.8.3.min.js"></script>
  <script src="http://passport.cnblogs.com/scripts/jsencrypt.min.js"></script>
  <script type="text/javascript">
   // Call this code when the page is done loading.
   $(function() {
    // Run a quick encryption/decryption when they click.
    $('#testme').click(function() {
     // Encrypt with the public key...
     var encrypt = new JSEncrypt();
     encrypt.setPublicKey($('#pubkey').val());
     var encrypted = encrypt.encrypt($('#input').val());
     // Decrypt with the private key...
     var decrypt = new JSEncrypt();
     decrypt.setPrivateKey($('#privkey').val());
     var uncrypted = decrypt.decrypt(encrypted);
     // Now a simple check to see if the round-trip worked.
     if (uncrypted == $('#input').val()) {
      alert('It works!!!');
     }
     else {
      alert('Something went wrong....');
     }
    });
   });
  </script>
 </head>
 <body>
  <label for="privkey">Private Key</label><br/>
  <textarea id="privkey" rows="15" cols="65">-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDlOJu6TyygqxfWT7eLtGDwajtNFOb9I5XRb6khyfD1Yt3YiCgQ
WMNW649887VGJiGr/L5i2osbl8C9+WJTeucF+S76xFxdU6jE0NQ+Z+zEdhUTooNR
aY5nZiu5PgDB0ED/ZKBUSLKL7eibMxZtMlUDHjm4gwQco1KRMDSmXSMkDwIDAQAB
AoGAfY9LpnuWK5Bs50UVep5c93SJdUi82u7yMx4iHFMc/Z2hfenfYEzu+57fI4fv
xTQ//5DbzRR/XKb8ulNv6+CHyPF31xk7YOBfkGI8qjLoq06V+FyBfDSwL8KbLyeH
m7KUZnLNQbk8yGLzB3iYKkRHlmUanQGaNMIJziWOkN+N9dECQQD0ONYRNZeuM8zd
8XJTSdcIX4a3gy3GGCJxOzv16XHxD03GW6UNLmfPwenKu+cdrQeaqEixrCejXdAF
z/7+BSMpAkEA8EaSOeP5Xr3ZrbiKzi6TGMwHMvC7HdJxaBJbVRfApFrE0/mPwmP5
rN7QwjrMY+0+AbXcm8mRQyQ1+IGEembsdwJBAN6az8Rv7QnD/YBvi52POIlRSSIM
V7SwWvSK4WSMnGb1ZBbhgdg57DXaspcwHsFV7hByQ5BvMtIduHcT14ECfcECQATe
aTgjFnqE/lQ22Rk0eGaYO80cc643BXVGafNfd9fcvwBMnk0iGX0XRsOozVt5Azil
psLBYuApa66NcVHJpCECQQDTjI2AQhFc1yRnCU/YgDnSpJVm1nASoRUnU8Jfm3Oz
uku7JUXcVpt08DFSceCEX9unCuMcT72rAQlLpdZir876
-----END RSA PRIVATE KEY-----</textarea><br/>
  <label for="pubkey">Public Key</label><br/>
  <textarea id="pubkey" rows="15" cols="65">-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlOJu6TyygqxfWT7eLtGDwajtN
FOb9I5XRb6khyfD1Yt3YiCgQWMNW649887VGJiGr/L5i2osbl8C9+WJTeucF+S76
xFxdU6jE0NQ+Z+zEdhUTooNRaY5nZiu5PgDB0ED/ZKBUSLKL7eibMxZtMlUDHjm4
gwQco1KRMDSmXSMkDwIDAQAB
-----END PUBLIC KEY-----</textarea><br/>
  <label for="input">Text to encrypt:</label><br/>
  <textarea id="input" name="input" type="text" rows=4 cols=70>This is a test!</textarea><br/>
  <input id="testme" type="button" value="Test Me!!!" /><br/>
 </body>
</html>

启动服务后访问http://127.0.0.1:5000/js_rsa_test即可。代码内容主要是先用jsencrypt.js加密数据,再进行解密,如果成功则弹出It works!!!对话框。不传输数据到后台,仅仅作为前端测试。

目录结构

│  my_private_rsa_key.bin
│  my_rsa_public.pem
│  RSA_demo.py
│  rsa_flask_demo.py

└─templates
        js_rsa_test.html
        rsa_view.html

Python 相关文章推荐
分享15个最受欢迎的Python开源框架
Jul 13 Python
Python实现的数据结构与算法之双端队列详解
Apr 22 Python
Python使用Supervisor来管理进程的方法
May 28 Python
django 常用orm操作详解
Sep 13 Python
一道python走迷宫算法题
Jan 22 Python
Python字符串格式化%s%d%f详解
Feb 02 Python
python贪婪匹配以及多行匹配的实例讲解
Apr 19 Python
Python pandas DataFrame操作的实现代码
Jun 21 Python
python实现简单聊天室功能 可以私聊
Jul 12 Python
使用django和vue进行数据交互的方法步骤
Nov 11 Python
python的slice notation的特殊用法详解
Dec 27 Python
python实现简单俄罗斯方块
Mar 13 Python
Python学习笔记之列表推导式实例分析
Aug 13 #Python
Django中create和save方法的不同
Aug 13 #Python
Python学习笔记之函数的定义和作用域实例详解
Aug 13 #Python
Python爬取智联招聘数据分析师岗位相关信息的方法
Aug 13 #Python
python基于json文件实现的gearman任务自动重启代码实例
Aug 13 #Python
Python 写入训练日志文件并控制台输出解析
Aug 13 #Python
基于MATLAB和Python实现MFCC特征参数提取
Aug 13 #Python
You might like
php实现分页工具类分享
2014/01/09 PHP
php生成随机颜色方法汇总
2014/12/03 PHP
php实现学生管理系统
2020/03/21 PHP
可缩放Reloaded-一个针对可缩放元素的复用组件
2007/03/10 Javascript
兼容IE与firefox火狐的回车事件(js与jquery)
2010/10/20 Javascript
JavaScript中使用构造器创建对象无需new的情况说明
2012/03/01 Javascript
在jQuery中使用$而避免跟其它库产生冲突的方法
2015/08/13 Javascript
js实现表单Radio切换效果的方法
2015/08/17 Javascript
jquery.validate[.unobtrusive]和Bootstrap实现tooltip错误提示问题分析
2016/10/30 Javascript
jQuery获取Table某列的值(推荐)
2017/03/03 Javascript
js实现图片旋转 js滚动鼠标中间对图片放大缩小
2017/07/05 Javascript
bootstrap fileinput插件实现预览上传照片功能
2018/01/23 Javascript
详解Node使用Puppeteer完成一次复杂的爬虫
2018/04/18 Javascript
js实现手表表盘时钟与圆周运动
2020/09/18 Javascript
Python中os和shutil模块实用方法集锦
2014/05/13 Python
python编写网页爬虫脚本并实现APScheduler调度
2014/07/28 Python
Python hashlib模块用法实例分析
2018/06/12 Python
numpy中loadtxt 的用法详解
2018/08/03 Python
python基于Selenium的web自动化框架
2019/07/14 Python
利用pytorch实现对CIFAR-10数据集的分类
2020/01/14 Python
python利用百度云接口实现车牌识别的示例
2020/02/21 Python
python爬虫scrapy框架的梨视频案例解析
2021/02/20 Python
纯css3无js实现的Android Logo(有简单动画)
2013/01/21 HTML / CSS
美国知名奢侈美容品牌零售商:Cos Bar
2017/04/21 全球购物
转党组织关系介绍信
2014/01/08 职场文书
毕业设计计划书
2014/01/09 职场文书
体育专业学生自我评价范文
2014/01/17 职场文书
后勤部经理岗位职责
2014/02/23 职场文书
孝敬父母的演讲稿
2014/05/14 职场文书
公司租房协议书范本
2014/10/08 职场文书
2014财务部年度工作总结
2014/12/08 职场文书
给领导的感谢信范文
2015/01/23 职场文书
《花钟》教学反思
2016/02/17 职场文书
2016年学校党支部创先争优活动总结
2016/04/05 职场文书
CSS 文字装饰 text-decoration & text-emphasis 详解
2021/04/06 HTML / CSS
Java Redisson多策略注解限流
2022/09/23 Java/Android