实验四 密码模块的应用

发布于:2024-12-09 ⋅ 阅读:(118) ⋅ 点赞:(0)

实验四 密码模块的应用

实践要求(40 分)

  1. 完成电子公文交换系统,系统功能,(15 分)
mindmap
  root((电子公文系统))
    发文
        公文起草
        公文查看
        发文审核(审核员)
        公文发送
        公文查询
    收文
        公文签收
        公文查看
        公文处理
        公文查询
    系统管理
        组织单位
        用户管理·
            操作员(科员)
            审核员(科长,处长):至少有一级审核功能
            系统管理员:至少管理用户账号,组织单位功能
            安全保密管理员:至少有权限管理功能,密钥管理功能
            安全审计员:至少有日志查看功能
        权限设置(安全保密管理员)
        系统日志
        数据字典(选做)

系统功能

  • 总体要求
    • 项目类型必须是B/S或C/S架构
    • 项目程序设计语言可以是C,Python,Rust等
  1. 三员制度是指将系统管理员、安全保密管理员和安全审计员三个角色分离,分别负责系统运行、安全保密和安全管理,相互制约,共同保障信息系统安全。三员职责
  • 系统管理员
    • 负责信息系统的日常维护、故障处理和升级更新。
    • 确保系统正常运行,对系统资源进行合理分配。
    • 负责用户账号的创建、修改和删除。
    • 定期备份重要数据,确保数据安全。
  • 安全保密管理员
    • 负责制定和实施安全保密策略,确保信息系统安全。
    • 对用户进行安全意识培训,提高用户安全防范能力。
    • 监控网络安全状况,发现异常情况及时处理。
    • 负责信息系统安全事件的应急响应和处理。
  • 安全审计员
    • 负责对信息系统进行安全审计,评估安全风险。
    • 监督系统管理员和安全保密管理员的工作,确保其履行职责。
    • 对信息系统安全事件进行调查,提出整改建议。
  1. 黄金法则(5 分 )
    • 身份鉴别:口令不能存,数据库要保存加盐的SM3Hash值
    • 访问控制:操作员,审核员,安全三员的权限设置
    • 安全审计:至少完成日志查询功能
  2. 密码(15 分)
    • 算法:SM2,SM3,SM4,推荐使用 Key
    • 密钥管理:所有私钥,对称算法密钥等不能明存
  3. 系统量化评估(5分)
  4. 提交要求:
  • 提交实践过程Markdown和转化的PDF文件
  • 代码,文档托管到gitee或github等,推荐 gitclone
  • 记录实验过程中遇到的问题,解决过程,反思等内容,用于后面实验报告

电子公文系统

  • 首先我们连接好数据库,运行代码,进入web服务界面
    在这里插入图片描述
  • web服务界面
    在这里插入图片描述
发文
  • 公文起草
    • 首先我们登入账号
      在这里插入图片描述
    • 进入界面首页
      在这里插入图片描述
    • 点击公文起草
      在这里插入图片描述
    • 填写标题内容、收文单位、选择密级
      在这里插入图片描述
    • 接着可以选择三个选项,取消后,将清除内容,我们先保存草稿
      在这里插入图片描述
    • 继续阅读
      在这里插入图片描述
    • 只是可以阅读,接着点击编辑,并且提交审核,状态变为“待审核”

在这里插入图片描述
在这里插入图片描述

  • 公文查看

    • 当前用户有权限查看所以我们直接点击“公文查找”
      在这里插入图片描述
    • 点击“阅读”,显示为只可阅读状态,完成公文查看
      在这里插入图片描述
  • 发文审核

    • 由于普通用户没有审核权限,我们切换为审核员的账号
      在这里插入图片描述
      在这里插入图片描述
    • 审核员登陆后可以点击“公文审核”,即可看到待处理的公文列表,点击我们的审核文稿
      在这里插入图片描述
    • 可进行审核,判定通过与否
      在这里插入图片描述
    • 审核后跳出该界面,可以进行下一操作,若不通过则无法进行公文签收,若通过则可进行公文签收
      在这里插入图片描述
  • 公文发送

    • 审核后文件会发出去,我们再次登入审核未通过的“17自我介绍”编辑,并且提交审核
      在这里插入图片描述
    • 提交审核后即可发出去,等待签收
      在这里插入图片描述
  • 公文查询

    • 点击公文查询,可以看到公文列表其中有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”


网站公告

今日签到

点亮在社区的每一天
去签到