实验四 密码模块的应用
实践要求(40 分)
- 完成电子公文交换系统,系统功能,(15 分)
mindmap
root((电子公文系统))
发文
公文起草
公文查看
发文审核(审核员)
公文发送
公文查询
收文
公文签收
公文查看
公文处理
公文查询
系统管理
组织单位
用户管理·
操作员(科员)
审核员(科长,处长):至少有一级审核功能
系统管理员:至少管理用户账号,组织单位功能
安全保密管理员:至少有权限管理功能,密钥管理功能
安全审计员:至少有日志查看功能
权限设置(安全保密管理员)
系统日志
数据字典(选做)
- 总体要求
- 项目类型必须是B/S或C/S架构
- 项目程序设计语言可以是C,Python,Rust等
- 三员制度是指将系统管理员、安全保密管理员和安全审计员三个角色分离,分别负责系统运行、安全保密和安全管理,相互制约,共同保障信息系统安全。三员职责
- 系统管理员
- 负责信息系统的日常维护、故障处理和升级更新。
- 确保系统正常运行,对系统资源进行合理分配。
- 负责用户账号的创建、修改和删除。
- 定期备份重要数据,确保数据安全。
- 安全保密管理员
- 负责制定和实施安全保密策略,确保信息系统安全。
- 对用户进行安全意识培训,提高用户安全防范能力。
- 监控网络安全状况,发现异常情况及时处理。
- 负责信息系统安全事件的应急响应和处理。
- 安全审计员
- 负责对信息系统进行安全审计,评估安全风险。
- 监督系统管理员和安全保密管理员的工作,确保其履行职责。
- 对信息系统安全事件进行调查,提出整改建议。
- 黄金法则(5 分 )
- 身份鉴别:口令不能存,数据库要保存加盐的SM3Hash值
- 访问控制:操作员,审核员,安全三员的权限设置
- 安全审计:至少完成日志查询功能
- 密码(15 分)
- 算法:SM2,SM3,SM4,推荐使用 Key
- 密钥管理:所有私钥,对称算法密钥等不能明存
- 系统量化评估(5分)
- 按照商用密码应用安全性评估量化评估规则,计算自己系统的得分,只计算应用和数据安全。
- 提交要求:
- 提交实践过程Markdown和转化的PDF文件
- 代码,文档托管到gitee或github等,推荐 gitclone
- 记录实验过程中遇到的问题,解决过程,反思等内容,用于后面实验报告
电子公文系统
- 首先我们连接好数据库,运行代码,进入web服务界面
- web服务界面
发文
- 公文起草
- 首先我们登入账号
- 进入界面首页
- 点击公文起草
- 填写标题内容、收文单位、选择密级
- 接着可以选择三个选项,取消后,将清除内容,我们先保存草稿
- 继续阅读
- 只是可以阅读,接着点击编辑,并且提交审核,状态变为“待审核”
- 首先我们登入账号
公文查看
- 当前用户有权限查看所以我们直接点击“公文查找”
- 点击“阅读”,显示为只可阅读状态,完成公文查看
- 当前用户有权限查看所以我们直接点击“公文查找”
发文审核
- 由于普通用户没有审核权限,我们切换为审核员的账号
- 审核员登陆后可以点击“公文审核”,即可看到待处理的公文列表,点击我们的审核文稿
- 可进行审核,判定通过与否
- 审核后跳出该界面,可以进行下一操作,若不通过则无法进行公文签收,若通过则可进行公文签收
- 由于普通用户没有审核权限,我们切换为审核员的账号
公文发送
- 审核后文件会发出去,我们再次登入审核未通过的“17自我介绍”编辑,并且提交审核
- 提交审核后即可发出去,等待签收
- 审核后文件会发出去,我们再次登入审核未通过的“17自我介绍”编辑,并且提交审核
公文查询
- 点击公文查询,可以看到公文列表其中有ID、标题、作者名称、发文单位、接受单位、最后的修改日期、状态、密级、操作等选项
- 点击公文查询,可以看到公文列表其中有ID、标题、作者名称、发文单位、接受单位、最后的修改日期、状态、密级、操作等选项
收文
公文签收
- 我们登入审核员,将审核通过的公文进行签收
- 点击“查看”并且进行签收
- 我们登入审核员,将审核通过的公文进行签收
公文查看
- 签收后也可以进行查看
- 签收后也可以进行查看
公文处理
- 我们可以选择签收或者拒收
- 我们可以选择签收或者拒收
系统管理
用户管理
操作员
- 可以进行公文查找和公文起草
- 可以进行公文查找和公文起草
审核员
- 可以进行公文审核与公文签收表
- 可以进行公文审核与公文签收表
系统管理员
- 可以进行用户管理
- 可以进行用户管理
安全保密管理员
- 可以进行权限管理
- 可以进行权限管理
安全审计员
- 可以查看日志以简单达到安全审计的目的
- 可以查看日志以简单达到安全审计的目的
权限设置
- 根据不同的用户给予不同的权限,只可以访问自己权限内的内容,点击其他内容被拒绝访问,而且日志中会进行记录并且警告
-- 插入角色数据
INSERT INTO Roles (role_name) VALUES ('sysAdmin'), ('secAdmin'),('secAuditor'),('auditor'), ('operator');
-- 插入权限数据
INSERT INTO Permissions (permission_name) VALUES ('userMgmt'), ('permMgmt'),('logMgmt'),('docReview'), ('docSign'),('docOps');
-- 插入用户-角色关联数据
INSERT INTO User_Role (user_id, role_id) VALUES
(1, 1),
(2, 2),
(3, 3),
(4, 4),
(5, 5),
(6, 1), (6, 2),(6, 3),(6, 4),(6, 5);
-- 插入角色-权限关联数据
INSERT INTO Role_Permission (role_id, permission_id) VALUES
(1, 1),
(2, 2),
(3, 3),
(4, 4),(4, 5),
(5, 6);
- 系统日志
以上是部分日志
黄金法则(5 分 )
- 身份鉴别
# 用户名:2 密码:1
# salt: 90d5729b8663780c3e79b89cbd1da8788baaf99687c197c0531cb57f429243ef
# password_hash: 00e09a28a51fee63af139ac750cc59b059701aac5586f1de7ce995a4b96f5019
INSERT INTO Users (Username, PasswordHash, Salt, Email,Unit)
VALUES ('20221408',
'00e09a28a51fee63af139ac750cc59b059701aac5586f1de7ce995a4b96f5019',
'90d5729b8663780c3e79b89cbd1da8788baaf99687c197c0531cb57f429243ef',
'20221408@mail.besti.edu.cn',
'单位A');
# 用户名:2 密码:1
# salt: 90d5729b8663780c3e79b89cbd1da8788baaf99687c197c0531cb57f429243ef
# password_hash: 00e09a28a51fee63af139ac750cc59b059701aac5586f1de7ce995a4b96f5019
INSERT INTO Users (Username, PasswordHash, Salt, Email,Unit)
VALUES ('20221414',
'00e09a28a51fee63af139ac750cc59b059701aac5586f1de7ce995a4b96f5019',
'90d5729b8663780c3e79b89cbd1da8788baaf99687c197c0531cb57f429243ef',
'',
'单位A');
INSERT INTO Users (Username, PasswordHash, Salt, Email,Unit)
VALUES ('20221403',
'00e09a28a51fee63af139ac750cc59b059701aac5586f1de7ce995a4b96f5019',
'90d5729b8663780c3e79b89cbd1da8788baaf99687c197c0531cb57f429243ef',
'20221403@mail.besti.edu.cn',
'单位B');
INSERT INTO Users (Username, PasswordHash, Salt, Email,Unit)
VALUES ('20221407',
'00e09a28a51fee63af139ac750cc59b059701aac5586f1de7ce995a4b96f5019',
'90d5729b8663780c3e79b89cbd1da8788baaf99687c197c0531cb57f429243ef',
'20221407@mail.besti.edu.cn',
'单位A');
INSERT INTO Users (Username, PasswordHash, Salt, Email,Unit)
VALUES ('20221417',
'00e09a28a51fee63af139ac750cc59b059701aac5586f1de7ce995a4b96f5019',
'90d5729b8663780c3e79b89cbd1da8788baaf99687c197c0531cb57f429243ef',
'20221417@mail.besti.edu.cn',
'单位B');
INSERT INTO Users (Username, PasswordHash, Salt, Email,Unit)
VALUES ('20221425',
'00e09a28a51fee63af139ac750cc59b059701aac5586f1de7ce995a4b96f5019',
'90d5729b8663780c3e79b89cbd1da8788baaf99687c197c0531cb57f429243ef',
'20221425@mail.besti.edu.cn',
'单位A');
# 密码:20221425
# salt: 53f8d3840f09fc9ab96113a1fe7ade2a338f72c54f5a204a87c02c264b21b1f0
# password_hash: 585d2c5176f7f3aea13a6e43616c351b770f48ac7e74d1d7450544771c10097f
# 密码:123456
# salt: 90d5729b8663780c3e79b89cbd1da8788baaf99687c197c0531cb57f429243ef
# password_hash: 00e09a28a51fee63af139ac750cc59b059701aac5586f1de7ce995a4b96f5019
这里的盐值一样的原因是因为使用统一的密码方便我进行演示,实际操作中testSM3.py会根据passwor的不同计算出不同的salt值储存在数据库中
- 访问控制
在上述的叙述中给出了具体实例,这里不过多赘述 - 安全审计已实现,看上面。
密码(15 分)
- 算法:SM2,SM3,SM4,推荐使用 Key
SM2算法则用来生成SM2 签名、验证签名的有效性、使用 SM2 公钥加密 SM4 密钥以及使用 SM2 私钥解密加密后的 SM4 密钥等功能
from gmssl import sm2, sm3, func
import binascii
import os
from gm_sm2_keygen import *
def sm3_hash_and_sign_dict(data_dict, private_key_hex, public_key_hex):
"""
对输入的字典数据进行SM3哈希计算,并使用SM2算法进行签名,返回哈希结果(十六进制字符串)和签名结果(十六进制字符串)。
需要调用者传入十六进制格式的私钥和公钥。
参数:
data_dict (dict): 需要处理的字典数据
private_key_hex (str): 十六进制格式的私钥
public_key_hex (str): 十六进制格式的公钥
返回:
tuple: 签名结果(十六进制字符串)的元组
"""
# 将字典数据转换为字节类型
data_bytes = str(data_dict).encode('utf-8')
# 校验传入的私钥和公钥是否为十六进制字符串且长度符合基本要求(简单校验,实际可更严格)
if not all(c in "0123456789abcdefABCDEF" for c in private_key_hex):
raise ValueError("私钥不是合法的十六进制字符串格式")
if not all(c in "0123456789abcdefABCDEF" for c in public_key_hex):
raise ValueError("公钥不是合法的十六进制字符串格式")
# 创建SM2对象实例,传入公私钥
sm2_crypt = sm2.CryptSM2(public_key=public_key_hex, private_key=private_key_hex)
# 进行签名操作,获取十六进制格式的签名结果
sign_sm3_result = sm2_crypt.sign_with_sm3(data_bytes)
return sign_sm3_result
def verify_dict_signature(data_dict, sign_value_hex, public_key_hex):
"""
验证给定字典数据对应的签名是否有效。
需要调用者传入十六进制格式的私钥、公钥以及签名值。
参数:
data_dict (dict): 原始的字典数据
sign_value_hex (str): 需要验证的签名值(十六进制字符串形式)
public_key_hex (str): 十六进制格式的公钥
返回:
bool: 验证结果,True表示签名有效,False表示签名无效
"""
# 将字典数据转换为字节类型
data_bytes = str(data_dict).encode('utf-8')
# 校验传入的公钥和签名值是否符合格式要求
if not all(c in "0123456789abcdefABCDEF" for c in public_key_hex):
raise ValueError("公钥不是合法的十六进制字符串格式")
if not all(c in "0123456789abcdefABCDEF" for c in sign_value_hex):
raise ValueError("签名值不是合法的十六进制字符串格式")
# 创建SM2对象实例,传入公钥和占位私钥
sm2_crypt = sm2.CryptSM2(public_key=public_key_hex, private_key=" ")
# 进行验证操作
verify_result = sm2_crypt.verify_with_sm3(sign_value_hex, data_bytes)
return verify_result
def encrypt_sm4_key_with_public_key(sm4_key, public_key_hex):
"""
使用给定的十六进制格式的公钥对SM4密钥进行加密,并返回字节类型的加密结果。
参数:
sm4_key (bytes): 需要加密的SM4密钥,字节类型
public_key_hex (str): 十六进制格式的公钥
返回:
bytes: 加密后的SM4密钥数据
"""
# 校验传入的公钥是否为十六进制字符串且长度符合基本要求(简单校验,实际可更严格)
if not all(c in "0123456789abcdefABCDEF" for c in public_key_hex):
raise ValueError("公钥不是合法的十六进制字符串格式")
# 创建SM2对象实例,传入公钥(私钥占位)
sm2_crypt = sm2.CryptSM2(public_key=public_key_hex, private_key='')
# 进行加密操作
encrypted_sm4_key = sm2_crypt.encrypt(sm4_key)
return encrypted_sm4_key
def decrypt_sm4_key_with_private_key(encrypted_sm4_key_bytes, private_key_hex):
"""
使用给定的十六进制格式的私钥对字节类型的加密后的SM4密钥进行解密,并返回十六进制字符串格式的解密结果。
参数:
encrypted_sm4_key_bytes (bytes): 字节类型的加密后的SM4密钥数据
private_key_hex (str): 十六进制格式的私钥
返回:
str: 十六进制格式的解密后的SM4密钥
"""
# 校验传入的私钥是否为十六进制字符串且长度符合基本要求(简单校验,实际可更严格)
if not all(c in "0123456789abcdefABCDEF" for c in private_key_hex):
raise ValueError("私钥不是合法的十六进制字符串格式")
# 创建SM2对象实例,传入私钥和占位公钥
sm2_crypt = sm2.CryptSM2(public_key='', private_key=private_key_hex)
# 进行解密操作
decrypted_sm4_key = sm2_crypt.decrypt(encrypted_sm4_key_bytes)
return binascii.hexlify(decrypted_sm4_key).decode('utf-8')
if __name__ == "__main__":
# 示例字典数据
test_dict = {"key1": "value1", "key2": "value2"}
# 示例十六进制格式的私钥和公钥(假设已有生成公私钥对的函数create_key_pair,实际需替换为真实有效的方式获取)
private_key_hex_example, public_key_hex_example = create_key_pair()
# 1. 进行SM3哈希和SM2签名操作
sign_result = sm3_hash_and_sign_dict(test_dict, private_key_hex_example, public_key_hex_example)
print("SM2签名结果:", sign_result)
# 2. 进行签名验证操作
is_valid_signature = verify_dict_signature(test_dict, sign_result, public_key_hex_example)
print("签名验证结果:", is_valid_signature)
# 3. 示例SM4密钥生成(16字节随机字节数据符合SM4密钥长度要求)
sm4_key_example = os.urandom(16)
print("SM4密钥生成",binascii.hexlify(sm4_key_example).decode('utf-8'))
# 4. 使用公钥加密SM4密钥
encrypted_sm4_key_result = encrypt_sm4_key_with_public_key(sm4_key_example, public_key_hex_example)
print("加密后的SM4密钥(字节类型):", encrypted_sm4_key_result)
# 5. 使用私钥解密SM4密钥
decrypted_sm4_key_result = decrypt_sm4_key_with_private_key(encrypted_sm4_key_result, private_key_hex_example)
print("解密后的SM4密钥(十六进制格式):", decrypted_sm4_key_result)
from random import SystemRandom
class CurveFp:
def __init__(self, A, B, P, N, Gx, Gy, name):
self.A = A
self.B = B
self.P = P
self.N = N
self.Gx = Gx
self.Gy = Gy
self.name = name
sm2p256v1 = CurveFp(
name="sm2p256v1",
A=0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC,
B=0x28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93,
P=0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF,
N=0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123,
Gx=0x32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7,
Gy=0xBC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0
)
def multiply(a, n, N, A, P):
return fromJacobian(jacobianMultiply(toJacobian(a), n, N, A, P), P)
def add(a, b, A, P):
return fromJacobian(jacobianAdd(toJacobian(a), toJacobian(b), A, P), P)
def inv(a, n):
if a == 0:
return 0
lm, hm = 1, 0
low, high = a % n, n
while low > 1:
r = high // low
nm, new = hm - lm * r, high - low * r
lm, low, hm, high = nm, new, lm, low
return lm % n
def toJacobian(Xp_Yp):
Xp, Yp = Xp_Yp
return (Xp, Yp, 1)
def fromJacobian(Xp_Yp_Zp, P):
Xp, Yp, Zp = Xp_Yp_Zp
z = inv(Zp, P)
return ((Xp * z ** 2) % P, (Yp * z ** 3) % P)
def jacobianDouble(Xp_Yp_Zp, A, P):
Xp, Yp, Zp = Xp_Yp_Zp
if not Yp:
return (0, 0, 0)
ysq = (Yp ** 2) % P
S = (4 * Xp * ysq) % P
M = (3 * Xp ** 2 + A * Zp ** 4) % P
nx = (M ** 2 - 2 * S) % P
ny = (M * (S - nx) - 8 * ysq ** 2) % P
nz = (2 * Yp * Zp) % P
return (nx, ny, nz)
def jacobianAdd(Xp_Yp_Zp, Xq_Yq_Zq, A, P):
Xp, Yp, Zp = Xp_Yp_Zp
Xq, Yq, Zq = Xq_Yq_Zq
if not Yp:
return (Xq, Yq, Zq)
if not Yq:
return (Xp, Yp, Zp)
U1 = (Xp * Zq ** 2) % P
U2 = (Xq * Zp ** 2) % P
S1 = (Yp * Zq ** 3) % P
S2 = (Yq * Zp ** 3) % P
if U1 == U2:
if S1 != S2:
return (0, 0, 1)
return jacobianDouble((Xp, Yp, Zp), A, P)
H = U2 - U1
R = S2 - S1
H2 = (H * H) % P
H3 = (H * H2) % P
U1H2 = (U1 * H2) % P
nx = (R ** 2 - H3 - 2 * U1H2) % P
ny = (R * (U1H2 - nx) - S1 * H3) % P
nz = (H * Zp * Zq) % P
return (nx, ny, nz)
def jacobianMultiply(Xp_Yp_Zp, n, N, A, P):
Xp, Yp, Zp = Xp_Yp_Zp
if Yp == 0 or n == 0:
return (0, 0, 1)
if n == 1:
return (Xp, Yp, Zp)
if n < 0 or n >= N:
return jacobianMultiply((Xp, Yp, Zp), n % N, N, A, P)
if (n % 2) == 0:
return jacobianDouble(jacobianMultiply((Xp, Yp, Zp), n // 2, N, A, P), A, P)
if (n % 2) == 1:
return jacobianAdd(jacobianDouble(jacobianMultiply((Xp, Yp, Zp), n // 2, N, A, P), A, P), (Xp, Yp, Zp), A, P)
class PrivateKey:
def __init__(self, curve=sm2p256v1, secret=None):
self.curve = curve
self.secret = secret or SystemRandom().randrange(1, curve.N)
def publicKey(self):
curve = self.curve
xPublicKey, yPublicKey = multiply((curve.Gx, curve.Gy), self.secret, A=curve.A, P=curve.P, N=curve.N)
return PublicKey(xPublicKey, yPublicKey, curve)
def toString(self):
return "{}".format(str(hex(self.secret))[2:].zfill(64))
class PublicKey:
def __init__(self, x, y, curve):
self.x = x
self.y = y
self.curve = curve
def toString(self, compressed=True):
return {
True: str(hex(self.x))[2:],
False: "{}{}".format(str(hex(self.x))[2:].zfill(64), str(hex(self.y))[2:].zfill(64))
}.get(compressed)
def create_key_pair():
priKey = PrivateKey()
pubKey = priKey.publicKey()
return priKey.toString(), pubKey.toString(compressed=False)
if __name__ == "__main__":
priKey = PrivateKey()
pubKey = priKey.publicKey()
print(priKey.toString())
print(pubKey.toString(compressed=False))
SM3用来计算用户输入password的hash值与salt值
from gmssl import sm3, func
def generate_salt(length=64):
"""生成指定长度的随机盐(十六进制字符串)"""
# 生成32字节的盐值,即64个十六进制字符
return func.random_hex(length)
def hash_with_salt(password, salt):
"""使用盐值对密码进行SM3哈希处理"""
# 将密码和盐拼接起来,并编码为字节
data = (password + salt).encode('utf-8')
# 使用SM3算法计算哈希
hash_value = sm3.sm3_hash(func.bytes_to_list(data))
# 返回十六进制字符串形式的哈希值
return hash_value
def verify_password(input_password, salt, stored_hash):
"""验证输入的密码是否与存储的哈希匹配"""
# 使用相同的盐值对输入密码进行哈希处理
input_hash = hash_with_salt(input_password, salt)
# 比较新生成的哈希与存储的哈希
# 使用常量时间比较防止计时攻击
return constant_time_compare(input_hash, stored_hash)
def constant_time_compare(val1, val2):
"""进行常量时间比较,防止计时攻击"""
if len(val1) != len(val2):
return False
result = 0
for x, y in zip(val1, val2):
result |= ord(x) ^ ord(y)
return result == 0
SM4算法基于国产密码算法 SM4(采用 CBC 模式)的多种数据加密和解密功能,包括对字典数据、文本数据以及模拟的 SM2 私钥数据进行加密和解密操作
import binascii
from gmssl.sm4 import CryptSM4, SM4_ENCRYPT, SM4_DECRYPT
import json
def hex_str_to_bytes(key_hex_str):
"""
将十六进制格式的字符串转换为16字节的字节类型数据
:param key_hex_str: 十六进制格式的字符串
:return: 字节类型数据
"""
return binascii.unhexlify(key_hex_str)
def check_key_iv_len(key, iv):
if isinstance(key, str):
key = hex_str_to_bytes(key)
assert len(key) == 16, "密钥长度必须为16字节"
assert isinstance(iv, bytes) and len(iv) == 16, "初始向量长度必须为16字节"
def sm4_encrypt_dict(key, input_dict, iv):
"""
使用SM4算法(CBC模式)加密字典数据
:param key: 加密密钥,支持十六进制格式字符串或字节类型,长度对应16字节
:param input_dict: 需要加密的字典数据
:param iv: 初始向量,字节类型,长度必须为16字节
:return: 加密后的字节数据
"""
if isinstance(key, str):
key = hex_str_to_bytes(key)
check_key_iv_len(key, iv)
# 将字典序列化为字符串后再编码为字节数据
data_to_encrypt = json.dumps(input_dict).encode('utf-8')
crypt_sm4 = CryptSM4(mode=SM4_ENCRYPT)
crypt_sm4.set_key(key, SM4_ENCRYPT)
return crypt_sm4.crypt_cbc(iv, data_to_encrypt)
def sm4_decrypt_dict(key, encrypted_data, iv):
"""
使用SM4算法(CBC模式)解密字节数据并还原为字典
:param key: 解密密钥,支持十六进制格式字符串或字节类型,长度对应16字节
:param encrypted_data: 加密后的字节数据
:param iv: 初始向量,字节类型,长度必须为16字节
:return: 解密并还原后的字典数据,如果还原失败返回None
"""
if isinstance(key, str):
key = hex_str_to_bytes(key)
check_key_iv_len(key, iv)
crypt_sm4 = CryptSM4(mode=SM4_DECRYPT)
crypt_sm4.set_key(key, SM4_DECRYPT)
decrypted_data = crypt_sm4.crypt_cbc(iv, encrypted_data)
try:
# 尝试将解密后的字节数据解码并反序列化为字典
return json.loads(decrypted_data.decode('utf-8'))
except json.JSONDecodeError:
print("解密后的数据无法正确还原为字典")
return None
def sm4_encrypt_text(key, text, iv):
"""
使用SM4算法(CBC模式)加密文本数据
:param key: 加密密钥,支持十六进制格式字符串或字节类型,长度对应16字节
:param text: 需要加密的文本字符串
:param iv: 初始向量,字节类型,长度必须为16字节
:return: 加密后的字节数据
"""
if isinstance(key, str):
key = hex_str_to_bytes(key)
check_key_iv_len(key, iv)
crypt_sm4 = CryptSM4(mode=SM4_ENCRYPT)
crypt_sm4.set_key(key, SM4_ENCRYPT)
return crypt_sm4.crypt_cbc(iv, text.encode('utf-8'))
def sm4_decrypt_text(key, encrypted_text, iv):
"""
使用SM4算法(CBC模式)解密字节数据并还原为文本
:param key: 解密密钥,支持十六进制格式字符串或字节类型,长度对应16字节
:param encrypted_text: 加密后的字节数据
:param iv: 初始向量,字节类型,长度必须为16字节
:return: 解密并还原后的文本字符串,如果还原失败返回None
"""
if isinstance(key, str):
key = hex_str_to_bytes(key)
check_key_iv_len(key, iv)
crypt_sm4 = CryptSM4(mode=SM4_DECRYPT)
crypt_sm4.set_key(key, SM4_DECRYPT)
decrypted_data = crypt_sm4.crypt_cbc(iv, encrypted_text)
try:
return decrypted_data.decode('utf-8')
except UnicodeDecodeError:
print("解密后的数据无法正确解码为文本")
return None
def sm2_private_key_encrypt(key, private_key, iv):
"""
使用SM4算法(CBC模式)加密SM2私钥(这里只是示例,实际情况私钥保密要求极高)
:param key: SM4加密使用的16字节密钥,支持十六进制格式字符串或字节类型,用于加密SM2私钥
:param private_key: SM2私钥,十六进制字符串形式
:param iv: 初始向量,字节类型,长度必须为16字节
:return: 加密后的结果,字节类型
"""
if isinstance(key, str):
key = hex_str_to_bytes(key)
check_key_iv_len(key, iv)
# 将十六进制字符串形式的私钥转换为字节类型(按十六进制解析)
private_key_bytes = binascii.unhexlify(private_key)
crypt_sm4 = CryptSM4(mode=SM4_ENCRYPT)
crypt_sm4.set_key(key, SM4_ENCRYPT)
return crypt_sm4.crypt_cbc(iv, private_key_bytes)
def sm2_private_key_decrypt(key, encrypted_private_key, iv):
"""
使用SM4算法(CBC模式)解密加密后的SM2私钥并还原
:param key: SM4解密使用的16字节密钥,支持十六进制格式字符串或字节类型,用于解密SM2私钥
:param encrypted_private_key: 已经加密的SM2私钥,字节类型
:param iv: 初始向量,字节类型,长度必须为16字节
:return: 解密后的SM2私钥,字节类型,如果解密失败返回None
"""
if isinstance(key, str):
key = hex_str_to_bytes(key)
check_key_iv_len(key, iv)
crypt_sm4 = CryptSM4(mode=SM4_DECRYPT)
crypt_sm4.set_key(key, SM4_DECRYPT)
try:
decrypted_bytes = crypt_sm4.crypt_cbc(iv, encrypted_private_key)
# 将解密后的字节数据转换为十六进制字符串形式(方便查看和后续可能的对比等操作)
return binascii.hexlify(decrypted_bytes).decode('utf-8')
except:
print("解密SM2私钥出现异常")
return None
- 密钥管理:所有私钥,对称算法密钥等不能明存
都存在了SDF硬件设备中
from ctypes import *
import skf_api as skf_def # 导入封装定义的模块,这里起了别名skf_def方便使用
# 加载动态库
skfapi_dll = cdll.LoadLibrary("mtoken_gm3000.dll")
# 设置函数原型
skf_def.set_function_prototypes(skfapi_dll)
def main():
ulRslt = skf_def.ULONG(skf_def.SAR_OK)
hdev = skf_def.HANDLE(None)
happ = skf_def.HANDLE(None)
hcont = skf_def.HANDLE(None)
hkey = skf_def.HANDLE(None)
szDevName = create_string_buffer(256)
szAppName = create_string_buffer(256)
szContName = create_string_buffer(256)
pUserPin = b"123456"
ulDevNameLen = skf_def.ULONG(256)
ulAppNameLen = skf_def.ULONG(256)
ulContNameLen = skf_def.ULONG(256)
ulRetryCount = skf_def.ULONG(0)
ulHashDataLen = skf_def.ULONG(256)
ecc_pub = skf_def.ECCPUBLICKEYBLOB()
ecc_sign = skf_def.ECCSIGNATUREBLOB()
ulEccpubLen = skf_def.ULONG(sizeof(skf_def.ECCPUBLICKEYBLOB))
# bp = skf_def.BLOCKCIPHERPARAM()
pappname = szAppName
idlen = 16
pdevname = szDevName
pcontname = szContName
# 变量类型转换:BYTE *pbData 同时需要改变上面的c_ubyte为POINTER(c_ubyte)。
pHashData = create_string_buffer(256)
pHashDataPointer = cast(pHashData, POINTER(c_ubyte))
pubid = b"1234567812345678"
# 获取pubid的长度
pubid_len = len(pubid)
# 创建一个c_ubyte类型的数组,长度与pubid字节串长度一致
pubid_array = (c_ubyte * pubid_len)(*pubid)
psrcdata = b"1234567812345678"
psrcdata_len = len(psrcdata)
psrcdata_array = (c_ubyte * psrcdata_len)(*psrcdata)
# 调用SKF_EnumDev函数
ulRslt.value = skfapi_dll.SKF_EnumDev(skf_def.BOOL(True), szDevName, byref(ulDevNameLen))
if ulRslt.value!= skf_def.SAR_OK:
return
# 调用SKF_ConnectDev函数
ulRslt.value = skfapi_dll.SKF_ConnectDev(pdevname, byref(hdev))
if ulRslt.value!= skf_def.SAR_OK:
return
# 调用SKF_EnumApplication函数
ulRslt.value = skfapi_dll.SKF_EnumApplication(hdev, szAppName, byref(ulAppNameLen))
if ulRslt.value!= skf_def.SAR_OK:
return
# 调用SKF_OpenApplication函数
ulRslt.value = skfapi_dll.SKF_OpenApplication(hdev, pappname, byref(happ))
if ulRslt.value!= skf_def.SAR_OK:
return
# 调用SKF_VerifyPIN函数
ulRslt.value = skfapi_dll.SKF_VerifyPIN(happ, skf_def.ULONG(skf_def.USER_TYPE), pUserPin, byref(ulRetryCount))
if ulRslt.value!= skf_def.SAR_OK:
return
# 调用SKF_EnumContainer函数
ulRslt.value = skfapi_dll.SKF_EnumContainer(happ, szContName, byref(ulContNameLen))
if ulRslt.value!= skf_def.SAR_OK:
return
# 调用SKF_OpenContainer函数
ulRslt.value = skfapi_dll.SKF_OpenContainer(happ, pcontname, byref(hcont))
if ulRslt.value!= skf_def.SAR_OK:
return
# 修正此处,添加正确的参数传递
buffer_size = skf_def.ULONG(1024) # 可以根据实际情况调整缓冲区大小
file_list_buffer = create_string_buffer(buffer_size.value)
file_name = file_list_buffer
# [HANDLE, ctypes.c_char_p, ctypes.POINTER(ULONG)]
ulRslt.value = skfapi_dll.SKF_EnumFiles(happ, file_list_buffer, byref(buffer_size))
if ulRslt.value != skf_def.SAR_OK:
return
print("buffer_size: ",buffer_size)
print("file_list_buffer: ",file_list_buffer)
# # 创建文件测试
file_name_create = b"test"
file_size_create = 1024
read_rights_create = 255
write_rights_create = 255
ulRslt.value = skfapi_dll.SKF_CreateFile(happ, file_name_create, file_size_create, read_rights_create,
write_rights_create)
if ulRslt.value == skf_def.SAR_OK:
print("文件创建成功")
else:
print(f"文件创建失败,错误码: {ulRslt.value}")
# 新增:调用SKF_GetFileInfo函数获取文件信息
file_info = skf_def.FILEATTRIBUTE()
# [HANDLE, ctypes.c_char_p, ctypes.POINTER(FILEATTRIBUTE)]
ulRslt.value = skfapi_dll.SKF_GetFileInfo(happ, file_name_create, byref(file_info))
if ulRslt.value == skf_def.SAR_OK:
print(f"文件名: {file_info.FileName.decode('utf-8')}")
print(f"文件大小: {file_info.FileSize}")
print(f"读权限: {file_info.ReadRights}")
print(f"写权限: {file_info.WriteRights}")
else:
print(f"获取文件信息失败,错误码: {ulRslt.value}")
# 写入文件测试
offset_write = 0
# 创建一个c_ubyte类型的数组,长度与pubid字节串长度一致
data = b"1234567812345678"
data_len = len(data)
data_array = (c_ubyte * data_len)(*data)
ulRslt.value = skfapi_dll.SKF_WriteFile(happ, file_name_create, offset_write, data_array, len(data_array))
if ulRslt.value == skf_def.SAR_OK:
print("文件写入成功")
else:
print(f"文件写入失败,错误码: {ulRslt.value}")
offset_read = 0
size_read = 16
buffer = (skf_def.ctypes.c_ubyte * size_read)() # 创建一个ctypes.c_ubyte类型的数组,用于存储读取的字节数据
buffer_pointer = cast(buffer, POINTER(skf_def.ctypes.c_ubyte))
actual_size_read = skf_def.ULONG(size_read) # 创建一个ULONG类型变量,用于接收实际读取长度
ulRslt.value = skfapi_dll.SKF_ReadFile(happ, file_name_create, offset_read, size_read, buffer_pointer, byref(actual_size_read))
if ulRslt.value == skf_def.SAR_OK:
print("文件读取成功,内容(十六进制表示):", [hex(byte) for byte in buffer[:actual_size_read.value]]) # 以十六进制形式打印读取到的字节数据,根据实际读取长度截取
else:
print(f"文件读取失败,错误码: {ulRslt.value}")
# 删除文件测试
ulRslt.value = skfapi_dll.SKF_DeleteFile(happ, file_name_create)
if ulRslt.value == skf_def.SAR_OK:
print("文件删除成功")
else:
print(f"文件删除失败,错误码: {ulRslt.value}")
# 关闭相关句柄和释放资源,对应原来C代码中的清理部分
if hkey:
skfapi_dll.SKF_CloseHandle(hkey)
if hcont:
skfapi_dll.SKF_CloseContainer(hcont)
if happ:
skfapi_dll.SKF_CloseApplication(happ)
if hdev:
skfapi_dll.SKF_DisConnectDev(hdev)
if __name__ == "__main__":
main()
系统量化评估(5分)
- 按照商用密码应用安全性评估量化评估规则,计算自己系统的得分,只计算应用和数据安全。
- 依据上表得出:
身份鉴别:1
访问控制信息完整性:0.4
重要信息资源安全标记完整性:0.7
重要数据存储机密性:0.4
重要数据传输完整性:0.7
重要数据存储完整性:0.7
不可否认性:1
综合得分:18.285
实验四实验报告(10 分)
参考附件中的实验报告模板,完成并提交实验报告,报告名称“学号_姓名_实验序号_实验名称.doc”