电子公文传输系统安全-进展一

news/2024/11/17 23:27:54/文章来源:https://www.cnblogs.com/icdamlkn/p/18190386

上周任务完成情况

  • 每人撰写并提交一份读书笔记
  • 小组撰写并提交一份加固计划书、一份安全性设计方案
  • 小组撰写并提交一份系统安全设计报告
  • 小组讨论原公文传输系统的问题不足和改进方向
  • 小组讨论新公文传输系统的修改方案和预期效果
  • 每人自行学习国密标准、算法知识和设计方法

原公文传输系统的问题不足和改进方向

  • 用户密码存储存在未加密问题,需要改进密码存储方案
  • 用户公私钥对存在未加密问题,需要重新商讨密钥存储方案
def register(request):if request.method == 'POST':id = request.POST['id']username_up = request.POST['username_up']email = request.POST['email']password_up = request.POST['password_up']priKey = PrivateKey()pubKey = priKey.publicKey()new_user = UserProfile.objects.create(id=id,username_up=username_up,email=email,password_up=password_up,public_key=pubKey.toString(compressed=False),  # 存储公钥private_key=priKey.toString(), # 存储私钥avatar='avatars/default_avatar.png')new_user.save()LogData = Log.objects.create(username = new_user.username_up,documentname = "无",operation = f'用户{new_user.username_up}于{timezone.now()}注册了账号。')LogData.save()# 添加成功消息messages.success(request, '注册成功,请登录。')time.sleep(3)return redirect('login')
  • 问题分析

    • 用户密码存储未加密问题:注册过程中,用户的密码(password_up)直接以明文形式存储在数据库中。这样做会使得用户的敏感信息暴露在数据库中,一旦数据库泄露,用户的密码将被直接获取,存在安全风险。
    • 用户公私钥对未加密问题: 注册过程中生成了用户的公私钥对(pubKeypriKey),并将其以明文形式存储在数据库中。同样地,这样做会导致用户的私钥等敏感信息被直接暴露在数据库中,存在泄露风险。
  • 为了解决这些问题,应该采取以下措施:

    • 密码加密存储:应该对用户的密码进行加密处理,通常采用的方法是使用哈希算法对密码进行哈希处理,然后再存储哈希值而不是明文密码。Django提供了内置的密码哈希算法和验证器,应该使用make_password方法对密码进行加密处理。
    • 公私钥对加密存储:不应该将用户的公私钥对以明文形式存储在数据库中。通常情况下,应该将私钥加密后再存储,而公钥可以公开存储。私钥应该由用户自行保管,不应该存储在数据库中。如果需要在服务端使用用户的私钥,应该采取适当的加密方式进行存储,确保私钥在数据库中是安全的。

用户密码存储方案

为实现用户密码的安全存储,我们小组现讨论出如下几种方案:

基于加盐bcrypt算法的用户密码安全存储方案

加盐哈希存储
  • 哈希算法:使用哈希算法(如SHA256、SHA512)将密码转换成固定长度的摘要,确保密码不会以明文形式存储,且哈希过程不可逆。
  • 加盐(Salt):在进行哈希运算前,为每个密码添加一个随机生成的盐值。盐值的作用是即使两个用户使用相同的密码,最终存储的哈希结果也会不同。
盐的生成策略
  • 盐值长度:盐值长度必须足够长,以防止攻击者通过预先生成的查询表进行攻击。推荐使用较长的盐值,例如16字节或更长。
  • 生成方法:使用加密安全伪随机生成器(Cryptographically Secure Pseudo-Random Number Generator, CSPRNG)生成盐值,确保其随机性和安全性。
慢哈希函数
  • bcrypt算法:采用bcrypt算法对密码进行加盐哈希存储。bcrypt算法通过增加迭代因子(work factor),降低运算速度,延长破解所需时间,从而预防暴力破解和彩虹表攻击。
  • bcrypt计算后的密文中包含了算法版本、迭代因子、盐值以及哈希串。
    示例:加密后的密码哈希值$2a$12$eoL7CAx5FXw8zxTOoVBVVVu8VdLq2G0zbssix3fnhh4wN5Pv8/MEX2,其中$2a$代表bcrypt算法版本,12$代表迭代因子,接下来的22个字符为盐值,剩余部分为加密哈希串。
用户登录
  • 用户输入登录名和密码:用户在登录时输入登录名和密码。
  • 获取存储的盐值和哈希值:系统从数据库中获取对应用户的盐值和加密后的密码哈希值。
  • 加盐哈希验证:使用获取的盐值和bcrypt算法对输入的密码进行加密。
  • 比对哈希值:将加密后的结果与数据库中存储的哈希值进行比对。

通过上述方案,确保了用户密码的安全存储,即使数据库被泄露,攻击者也难以通过暴力破解和彩虹表攻击获取用户的实际密码。

基于随机替换增强的MD5加盐哈希密码存储方案

加盐MD5哈希
  • 初始哈希处理:
    • 用户输入密码,系统使用MD5算法对其进行哈希计算,生成32位16进制数的MD5值。
  • 提取和替换:
    • 从生成的MD5值中提取第5至第19位字符。
    • 随机生成一个15位的字符串,用于替换提取出的MD5值部分。
  • 存储处理:
    • 将替换后的新MD5值存储在数据库中。
具体流程示例
  • 用户注册:
    • 输入密码000000,通过MD5算法转换为670b14728ad9902aecba32e22fa4f6bd。
    • 提取第5至第19位,得到14728ad9902ae。
    • 随机生成字符串c11wssfgyyj%swg。
    • 替换提取部分,得到新的MD5值670c11wssfgyyj%swgba32e22fa4f6bd。
    • 将新的MD5值存储在数据库中。
  • 用户登录:
    • 输入密码000000,通过MD5算法转换为670b14728ad9902aecba32e22fa4f6bd。
    • 提取第5至第19位,得到14728ad9902ae。
    • 使用与注册时相同的方法生成字符串c11wssfgyyj%swg。
    • 替换提取部分,得到新的MD5值670c11wssfgyyj%swgba32e22fa4f6bd。
    • 将新的MD5值与数据库中存储的MD5值进行比对,验证通过。

C语言实现:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/md5.h>// 生成MD5值
void compute_md5(const char *str, char *md5_result) {unsigned char md5_digest[MD5_DIGEST_LENGTH];MD5((unsigned char *)str, strlen(str), md5_digest);for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {sprintf(&md5_result[i*2], "%02x", md5_digest[i]);}
}// 生成随机字符串
void generate_random_string(char *random_string, size_t length) {const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";for (size_t i = 0; i < length; i++) {int key = rand() % (int)(sizeof(charset) - 1);random_string[i] = charset[key];}random_string[length] = '\0';
}// 用户密码存储方案
void store_password(const char *password, char *stored_password) {char md5_result[MD5_DIGEST_LENGTH * 2 + 1];compute_md5(password, md5_result);char extracted_part[15 + 1];strncpy(extracted_part, md5_result + 4, 15);extracted_part[15] = '\0';char random_string[15 + 1];generate_random_string(random_string, 15);strncpy(stored_password, md5_result, 4);strncpy(stored_password + 4, random_string, 15);strncpy(stored_password + 19, md5_result + 19, 13);stored_password[32] = '\0';
}int main() {const char *password = "your_password";char stored_password[33];store_password(password, stored_password);printf("Stored password: %s\n", stored_password);return 0;
}

用户登录验证:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/md5.h>// 前面函数同上int verify_password(const char *password, const char *stored_password) {char md5_result[MD5_DIGEST_LENGTH * 2 + 1];compute_md5(password, md5_result);char extracted_part[15 + 1];strncpy(extracted_part, md5_result + 4, 15);extracted_part[15] = '\0';char random_string[15 + 1];strncpy(random_string, stored_password + 4, 15);random_string[15] = '\0';char verification_password[33];strncpy(verification_password, md5_result, 4);strncpy(verification_password + 4, random_string, 15);strncpy(verification_password + 19, md5_result + 19, 13);verification_password[32] = '\0';return strcmp(verification_password, stored_password) == 0;
}int main() {const char *password = "your_password";char stored_password[33];store_password(password, stored_password);// 模拟用户登录验证if (verify_password(password, stored_password)) {printf("Password verified successfully.\n");} else {printf("Password verification failed.\n");}return 0;
}

Python实现:

import hashlib
import random
import stringdef compute_md5(password):md5_hash = hashlib.md5(password.encode()).hexdigest()return md5_hashdef generate_random_string(length):characters = string.ascii_letters + string.digitsrandom_string = ''.join(random.choice(characters) for _ in range(length))return random_stringdef store_password(password):md5_hash = compute_md5(password)extracted_part = md5_hash[4:19]random_string = generate_random_string(15)stored_password = md5_hash[:4] + random_string + md5_hash[19:]return stored_password# 示例
password = "your_password"
stored_password = store_password(password)
print(f"Stored password: {stored_password}")

用户登录验证:

# 前面函数同上def verify_password(password, stored_password):md5_hash = compute_md5(password)extracted_part = md5_hash[4:19]random_string = stored_password[4:19]verification_password = md5_hash[:4] + random_string + md5_hash[19:]return verification_password == stored_password# 示例
password = "your_password"
stored_password = store_password(password)# 模拟用户登录验证
if verify_password(password, stored_password):print("Password verified successfully.")
else:print("Password verification failed.")

密钥存储方案

为了正确管理密钥,确保秘密信息不能明存,我们小组讨论了多种方案,其中几种有可行性的如下:

调用龙脉智能钥匙中的算法加密密钥

本方案为最终选定的密钥存储方案。
我们可以调用龙脉智能钥匙中的SM2算法加密公文传输系统中的对称密钥,调用SM1算法加密公文传输系统中的私钥。
我们的公文传输系统是用Python代码的Django框架搭建的,而调用龙脉智能钥匙的代码是C语言的,所以我们需要在Python代码中嵌入C语言代码,并编译运行C语言代码,以调用龙脉智能钥匙中的加密算法加密密钥。
我们的代码需要在Windows环境下运行,所以需要将C语言代码编译成可执行文件.exe的格式,命令行为:gcc XXX.c -o XXX.exe,我们需要使用-I指定头文件的路径,使用-L指定动态库的路径。

  • 利用龙脉key实现对私钥的加密
#include "../include/skfapi.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define	TRUE	1
#define FALSE	0
#define ERROR_THROW(r) {if((r) != SAR_OK) goto END_OF_FUN;}void encrypt_privateKey(const char* private_key)
{ULONG ulRslt = SAR_OK;HANDLE hdev = NULL;HANDLE happ = NULL;HANDLE hkey = NULL;HANDLE hcont = NULL;char   szDevName[256] = { 0 };ULONG	ulDevNameLen = 256;char	szAppName[256] = { 0 };ULONG	ulAppNameLen = 256;char	szContName[256] = { 0 };ULONG	ulContName = 256;char* pUserPin = "12345678";ULONG	ulRetryCount = 0;BYTE	pbEncrypt[256] = { 0 };ULONG   ulEncryptLen = 256;BYTE    pbRandom[32] = { 0 };BLOCKCIPHERPARAM bp = { 0 };int  nDatalen = strlen(private_key);char* pContName = szContName;char* pdevname = szDevName;char* pappname = szAppName;ulRslt = SKF_EnumDev(TRUE, szDevName, &ulDevNameLen);ERROR_THROW(ulRslt)ulRslt = SKF_ConnectDev(pdevname, &hdev);ERROR_THROW(ulRslt)ulRslt = SKF_EnumApplication(hdev, szAppName, &ulAppNameLen);ERROR_THROW(ulRslt)ulRslt = SKF_OpenApplication(hdev, pappname, &happ);ERROR_THROW(ulRslt)ulRslt = SKF_VerifyPIN(happ, USER_TYPE, pUserPin, &ulRetryCount);ERROR_THROW(ulRslt)ulRslt = SKF_EnumContainer(happ, szContName, &ulContName);ERROR_THROW(ulRslt)ulRslt = SKF_OpenContainer(happ, pContName, &hcont);ERROR_THROW(ulRslt)ulRslt = SKF_GenRandom(hdev, pbRandom, 16);ERROR_THROW(ulRslt)ulRslt = SKF_SetSymmKey(hdev, pbRandom, SGD_SM1_ECB, &hkey);ERROR_THROW(ulRslt)//bp.PaddingType = 1;ulRslt = SKF_EncryptInit(hkey, bp);ERROR_THROW(ulRslt)ulRslt = SKF_Encrypt(hkey, (BYTE*)private_key, Datalen, pbEncrypt, &ulEncryptLen);ERROR_THROW(ulRslt)printf("encrypt data ok!\n");char* encrypted_key = pbEncrypt;return encrypted_key;END_OF_FUN:if (hkey)SKF_CloseHandle(hkey);if (hcont)SKF_CloseContainer(hcont);if (happ)SKF_CloseApplication(happ);if (hdev)SKF_DisConnectDev(hdev);return 1;
}
  • 解密私钥
#include "../include/skfapi.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define	TRUE	1
#define FALSE	0
#define ERROR_THROW(r) {if((r) != SAR_OK) goto END_OF_FUN;}void decrypt_privateKey(const char* private_key_encoded)
{ULONG ulRslt = SAR_OK;HANDLE hdev = NULL;HANDLE happ = NULL;HANDLE hkey = NULL;HANDLE hcont = NULL;char   szDevName[256] = { 0 };ULONG	ulDevNameLen = 256;char	szAppName[256] = { 0 };ULONG	ulAppNameLen = 256;char	szContName[256] = { 0 };ULONG	ulContName = 256;char* pUserPin = "12345678";ULONG	ulRetryCount = 0;BYTE	pbnDcrypt[256] = { 0 };ULONG   ulDecryptLen = 256;BYTE    pbRandom[32] = { 0 };BLOCKCIPHERPARAM bp = { 0 };int  nDatalen = strlen(private_key_encoded);char* pContName = szContName;char* pdevname = szDevName;char* pappname = szAppName;ulRslt = SKF_EnumDev(TRUE, szDevName, &ulDevNameLen);ERROR_THROW(ulRslt)ulRslt = SKF_ConnectDev(pdevname, &hdev);ERROR_THROW(ulRslt)ulRslt = SKF_EnumApplication(hdev, szAppName, &ulAppNameLen);ERROR_THROW(ulRslt)ulRslt = SKF_OpenApplication(hdev, pappname, &happ);ERROR_THROW(ulRslt)ulRslt = SKF_VerifyPIN(happ, USER_TYPE, pUserPin, &ulRetryCount);ERROR_THROW(ulRslt)ulRslt = SKF_EnumContainer(happ, szContName, &ulContName);ERROR_THROW(ulRslt)ulRslt = SKF_OpenContainer(happ, pContName, &hcont);ERROR_THROW(ulRslt)ulRslt = SKF_GenRandom(hdev, pbRandom, 16);ERROR_THROW(ulRslt)ulRslt = SKF_SetSymmKey(hdev, pbRandom, SGD_SM1_ECB, &hkey);ERROR_THROW(ulRslt)//bp.PaddingType = 1;ulRslt = SKF_DecryptInit(hkey, bp);ERROR_THROW(ulRslt)ulRslt = SKF_Decrypt(hkey, (BYTE*)private_key_encoded, Datalen, pbDncrypt, &ulDecryptLen);ERROR_THROW(ulRslt)printf("decrypt data ok!\n");char* decrypted_key = pbDecrypt;return decrypted_key;END_OF_FUN:if (hkey)SKF_CloseHandle(hkey);if (hcont)SKF_CloseContainer(hcont);if (happ)SKF_CloseApplication(happ);if (hdev)SKF_DisConnectDev(hdev);return 1;
}
  • 加密保护私钥
import subprocess
import ctypes
# 编译SM1_PrivateKey C 代码
compile_command = "gcc -shared -o libsm1.dll sm1_encrypt.c -Llib/windows/lib -lmtoken_gm3000"# 加载动态链接库,注意这里只需要加载编译后的libsm2.dll,而不需要加载 Ukey 动态链接库
lib = ctypes.cdll.LoadLibrary('./libsm1.ddl')# 定义函数返回类型
lib.encrypt_private_key.restype = ctypes.c_char_p# 调用C函数
private_key = "your_private_key_here"
encrypted_private_key = lib.encrypt_private_key(private_key.encode('utf-8'))# 将加密后的私钥从字节串解码为字符串
encrypted_private_key = encrypted_private_key.decode('utf-8')print("Encrypted private key:", encrypted_private_key)

基于Cache的密钥安全方案

我们还考虑改变密钥的硬件存储位置提高密钥的安全性。通过将对称密钥和私钥存储到Cache中,而非内存中,防止针对内存空间的密钥提取攻击,保护密钥的安全。
我们使用的针对国密算法的Copker方案,方案框架如下图所示。

我们采用CAR技术,把Cache当作RAM使用,将所有敏感数据,例如:私钥、中间状态、随机数种子等,在整个存储期和计算期内锁定在Cache中处理,而不触及RAM保证密钥和其它敏感数据不以明文形态出现在内存中。
我们采用两层密钥体系,分别为SM4主密钥和私钥。SM4主密钥存储在CPU的特权寄存器中。私钥加密后存储在内存中,只在需要使用时解密到Cache中。
我们采用的方案执行密钥相关操作的过程必须是原子的,在操作过程中不允许产生任何形式的中断,且在执行操作前会完成栈空间的构造,操作开始时,会将进程的栈切换到提前预留的Cache中的栈空间中,保证所有计算均在Cache中进行、所有敏感数据和中间状态均存储在Cache中,避免敏感数据同步到主存。

调用bouncycastle库加密密钥

利用java包bouncycastle中的国密sm4算法加密sm2的私钥,并将加密后的私钥存储在数据库中。但由于bouncycastle为java平台的库,我们本次网站部署使用的是python代码,因此在两个语言平台之间替换不方便,因此不优先考虑使用bouncycastle的库。

本周计划

  • 改进密码存储方案,解决用户密码存储未加密问题
  • 重新商讨密钥存储方案,解决用户公私钥对存在未加密问题
  • 修改css文件,美化前端页面
  • 完善后端数据库

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/708473.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

存储监控软件stor2rrd

stor2rrd Stor2rrd是一种开源软件工具,用于监视和报告性能存储系统,SAN开关, 和局域网开关. 它由捷克公司Xorux开发;该工具支持来自主要存储供应商的企业级存储设备;例如:IBM、Dell EMC、NetAPP、HPE、Hitachi、Lenovo、Pure Storage、Huawei、Dot Hill、INFINIDAT、Fuji…

AtCoder Beginner Contest 354

rating++A - Exponential Plant (abc354 A) 题目大意 某星球上的植物,初始高\(0\),然后每天依次增长 \(1,2,4,8,...\),问哪天就高过身高为\(h\)的高桥。 解题思路 因为是指数级别长高,枚举一下天数即可,由于\(h \leq 10^9\),因此天数不会超过 \(32\)天。神奇的代码 #incl…

鸿蒙HarmonyOS实战-Stage模型(开发卡片页面)

🚀一、开发卡片页面 HarmonyOS元服务卡片页面(Metaservice Card Page)是指在HarmonyOS系统中,用于展示元服务的页面界面。元服务是指一组提供特定功能或服务的组件,例如天气服务、音乐播放服务等。元服务卡片页面可以显示元服务的相关信息和操作选项,用户可以通过点击卡…

连接池设计与实现一——以Golang Http1.1为例

0. 前言 连接池是一个非常重要的开发思想,如http client会构建连接池复用底层TCP连接,使用database/sql的使用也会有连接池的配置。那么代码底层是如何实现连接池的呢?这篇文档将以Golang语言为基础,分析http1.1连接池底层实现 ★ 注意:我们仅仅关注连接池设计思想、以及关…

Spring 面向切面编程AOP 详细讲解

1. Spring 面向切面编程AOP 详细讲解 @目录1. Spring 面向切面编程AOP 详细讲解每博一文案2. AOP介绍说明2.1 AOP的七大术语2.2 AOP 当中的 切点表达式3. 使用Spring 对 AOP 的实现使用3.1 准备工作3.2 Spring 基于AspectJ的AOP注解式开发3.2.1 实现步骤3.2.2 各个通知类型的说…

vulnhub - NYX: 1

标准攻击链vulnhub - NYX: 1 描述 这是一个简单的盒子,非常基本的东西。 它是基于vmware的,我不知道它是否可以在VB上运行,如果你愿意的话可以测试一下。 /home/$user/user.txt 和 /root/root.txt 下有 2 个标志。 信息收集 NYX靶ip:192.168.157.159 nmap -sT -sV -sC -O -p…

Vue3学习之项目工程初步了解

1.介绍项目工程,如在vscode中下图

因更懂你而更热爱,智慧PC助力当代每一位“霞客”行

“大丈夫当朝碧海而暮苍梧”,从小就立志游历天下的徐霞客,自公元1613年5月19日这天开始了一生的披荆斩棘。三十年时间素履而往,所有足迹凝练出了一本《徐霞客游记》,在地理学、地质学、文学、历史学中都占有重要一席。因热爱而出发,如果他生在当下,一定是中国最大的旅游博…

Pyqt6PySide6 事件与事件的处理函数

什么是事件? 事件是程序收到外界的输入,处于某种状态时自动发送的信号。事件有固定的类型,每种类型有自己的处理函数,用户只要重写这些函数,即可达到特定的目的。通过事件可以用一个控件监测另外一个控件,并可过滤被监测控件发出的事件。 事件的类型与处理函数 事件的概念…

C语言编程题练习 (从初识到入门再到进阶)修正版

C语言编程题练习 📔(从初识到入门再到进阶) 小飞机 🛬#include<stdio.h>int main() {printf(" ** \n");printf(" ** \n");printf("***************\n");printf("***************\n");printf("…

生物医学顶刊论文(JBHI-2024):TransFOL:药物相互作用中复杂关系推理的逻辑查询模型

(2024.5.17)JBHI-TransFOL:药物相互作用中复杂关系推理的逻辑查询模型 论文题目:TransFOL: A Logical Query Model for Complex Relational Reasoning in Drug-Drug Interaction 论文期刊:Journal of Biomedical and Health Informatics (JBHI) 论文地址:https://ieeexplor…

2024-05-18:用go语言,给定一个从 0 开始的字符串 s,以及两个子字符串 a 和 b,还有一个整数 k。 定义一个“美丽下标”,当满足以下条件时: 1.找到字符串 a 在字符串 s 中的位

2024-05-18:用go语言,给定一个从 0 开始的字符串 s,以及两个子字符串 a 和 b,还有一个整数 k。 定义一个“美丽下标”,当满足以下条件时: 1.找到字符串 a 在字符串 s 中的位置,且该位置范围为 0 <= i <= s.length - a.length。 2.找到字符串 b 在字符串 s 中的位置…