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 相关文章推荐
python实现连接mongodb的方法
May 08 Python
Python数据库的连接实现方法与注意事项
Feb 27 Python
Python 数据结构之堆栈实例代码
Jan 22 Python
详解python中asyncio模块
Mar 03 Python
flask中的wtforms使用方法
Jul 21 Python
python:动态路由的Flask程序代码
Nov 22 Python
PyTorch 解决Dataset和Dataloader遇到的问题
Jan 08 Python
python实现替换word中的关键文字(使用通配符)
Feb 13 Python
Pytorch通过保存为ONNX模型转TensorRT5的实现
May 25 Python
Python使用requests模块爬取百度翻译
Aug 25 Python
Python如何执行系统命令
Sep 23 Python
总结Python连接CS2000的详细步骤
Jun 23 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
全国FM电台频率大全 - 17 湖北省
2020/03/11 无线电
PHP控制反转(IOC)和依赖注入(DI)
2017/03/13 PHP
PHP中phar包的使用教程
2017/06/14 PHP
Javascript 遍历对象中的子对象
2009/07/03 Javascript
幻灯片带网页设计中的20个奇妙应用示例小结
2012/05/27 Javascript
JavaScript打印iframe内容示例代码
2013/08/20 Javascript
jQuery实现不断闪烁文字的方法
2015/05/15 Javascript
简单分析javascript面向对象与原型
2015/05/21 Javascript
快速学习AngularJs HTTP响应拦截器
2015/12/31 Javascript
基于JavaScript实现添加到购物车效果附源码下载
2016/08/22 Javascript
JavaScript中子对象访问父对象的方式详解
2016/09/01 Javascript
Angular 2应用的8个主要构造块有哪些
2016/10/17 Javascript
vuejs开发组件分享之H5图片上传、压缩及拍照旋转的问题处理
2017/03/06 Javascript
Vue 2中ref属性的使用方法及注意事项
2017/06/12 Javascript
vue中将网页打印成pdf实例代码
2017/06/15 Javascript
vue组件学习教程
2017/09/09 Javascript
慕课网题目之js实现抽奖系统功能
2017/09/19 Javascript
浅谈Angular2 模块懒加载的方法
2017/10/04 Javascript
vue获取当前点击的元素并传值的实例
2018/03/09 Javascript
JS实现的冒泡排序,快速排序,插入排序算法示例
2019/03/02 Javascript
Layui实现带查询条件的分页
2019/07/27 Javascript
[02:44]DOTA2英雄基础教程 钢背兽
2013/12/19 DOTA
跟老齐学Python之编写类之三子类
2014/10/11 Python
使用pycharm设置控制台不换行的操作方法
2019/01/19 Python
详解Python函数式编程—高阶函数
2019/03/29 Python
windows10下安装TensorFlow Object Detection API的步骤
2019/06/13 Python
美国二手复古奢侈品包包购物网站:LXRandCo
2019/06/18 全球购物
会计主管岗位职责范文
2013/11/08 职场文书
自主招生自荐信指南
2014/02/04 职场文书
行政文秘岗位职责范本
2014/02/10 职场文书
幼儿园教师教育感言
2014/02/28 职场文书
给孩子的新年寄语
2014/04/08 职场文书
班级寄语大全
2014/04/10 职场文书
交通事故赔偿协议书
2014/10/16 职场文书
入党介绍人意见怎么写
2015/06/03 职场文书
MySQL主从搭建(多主一从)的实现思路与步骤
2021/05/13 MySQL