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字典排序实例详解
May 20 Python
Python Requests 基础入门
Apr 07 Python
Python打包可执行文件的方法详解
Sep 19 Python
基于Django模板中的数字自增(详解)
Sep 05 Python
获取python的list中含有重复值的index方法
Jun 27 Python
详解python的sorted函数对字典按key排序和按value排序
Aug 10 Python
Pycharm取消py脚本中SQL识别的方法
Nov 29 Python
python把ipynb文件转换成pdf文件过程详解
Jul 09 Python
Python 异步协程函数原理及实例详解
Nov 13 Python
python从Oracle读取数据生成图表
Oct 14 Python
python tkinter模块的简单使用
Apr 07 Python
python处理json数据文件
Apr 11 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木马webshell扫描器代码
2012/01/25 PHP
PHP删除非空目录的函数代码小结
2013/02/28 PHP
解析PHP无限级分类方法及代码
2013/06/21 PHP
php安装swoole扩展的方法
2015/03/19 PHP
PHP经典面试题之设计模式(经常遇到)
2015/10/15 PHP
php经典算法集锦
2015/11/14 PHP
PHP符合PSR编程规范的实例分享
2016/12/21 PHP
详解PHP函数 strip_tags 处理字符串缺陷bug
2017/06/11 PHP
laravel实现按月或天或小时统计mysql数据的方法
2019/10/09 PHP
ThinkPHP5与单元测试PHPUnit使用详解
2020/02/23 PHP
javascript背投广告代码的完善
2008/04/08 Javascript
function foo的原型与prototype属性解惑
2010/11/19 Javascript
Javascript base64编码实现代码
2011/12/02 Javascript
Javascript的各种节点操作实例演示代码
2012/06/27 Javascript
浅谈javascript语法和定时函数
2015/05/03 Javascript
jQuery实现进度条效果代码
2015/12/17 Javascript
Bootstrap CDN和本地化环境搭建
2016/10/26 Javascript
微信小程序 判断手机号的实现代码
2017/04/19 Javascript
jquery.uploadView 实现图片预览上传功能
2017/08/10 jQuery
Vue filter介绍及详细使用
2018/04/04 Javascript
vue .js绑定checkbox并获取、改变选中状态的实例
2018/08/24 Javascript
JavaScript原型链与继承操作实例总结
2018/08/24 Javascript
jQuery实现带3D切割效果的轮播图功能示例【附源码下载】
2019/04/04 jQuery
微信小程序wxs实现吸顶效果
2020/01/08 Javascript
[01:20:38]完美世界DOTA2联赛 GXR vs IO 第一场 11.07
2020/11/09 DOTA
Python网络爬虫项目:内容提取器的定义
2016/10/25 Python
keras处理欠拟合和过拟合的实例讲解
2020/05/25 Python
Python如何对齐字符串
2020/07/30 Python
Python如何定义有默认参数的函数
2020/08/10 Python
Python+pyftpdlib实现局域网文件互传
2020/08/24 Python
实现strstr功能,即在父串中寻找子串首次出现的位置
2016/08/05 面试题
前台文员我鉴定
2014/01/12 职场文书
岗位职责风险防控
2014/02/18 职场文书
毕业生欢送会主持词
2014/03/31 职场文书
英文慰问信
2015/02/14 职场文书
礼仪培训心得体会
2016/01/22 职场文书