熵密杯 2025

也是拿到了第一,可惜后面打的两眼昏花审最后一题代码的时候审差了,或许就可以 ak 了 /(ㄒoㄒ)/~~

image-20250720163054224

相比去年和前年,今年的难度是真的高,代码量也多了很多,打的也是两眼昏花。其中打*的是赛中没有出的。

初始谜题

总共 3 个初始谜题,只有做了其中一个才可以进入下面的密码系统进行做题。

初始谜题1

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import binascii
from pyasn1.codec.der.decoder import decode
from pyasn1.type import univ, namedtype
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from gmssl import sm3, func, sm2
from pyasn1.codec.der.encoder import encode


class SM2Cipher(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('xCoordinate', univ.Integer()), # -- x 分量
namedtype.NamedType('yCoordinate', univ.Integer()), # -- y 分量
namedtype.NamedType('hash', univ.OctetString()), # --哈希值
namedtype.NamedType('cipherText', univ.OctetString()) # -- SM4密钥密文
)

class EncryptedData(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('algorithm', univ.ObjectIdentifier('1.2.156.10197.1.104.2')), # -- SM4-CBC OID
namedtype.NamedType('iv', univ.OctetString()), # -- SM4-CBC加密使用的初始化向量(IV)
namedtype.NamedType('cipherText', univ.OctetString()) # -- SM4加密的密文
)

class EnvelopedData(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('encryptedKey', SM2Cipher()), # -- 使用SM2公钥加密SM4密钥的密文
namedtype.NamedType('encryptedData', EncryptedData()), # -- 使用SM4密钥对明文加密的密文
namedtype.NamedType('digestAlgorithm', univ.ObjectIdentifier('1.2.156.10197.1.401.1')), # -- SM3算法OID
namedtype.NamedType('digest', univ.OctetString()) # -- 对明文计算的摘要值
)

def sm4_cbc_encrypt(plaintext: bytes, key: bytes, iv: bytes):
backend = default_backend()
cipher = Cipher(algorithms.SM4(key), modes.CBC(iv), backend=backend) #填充模式 nopadding
encryptor = cipher.encryptor()
ciphertext = encryptor.update(plaintext) + encryptor.finalize()
return ciphertext

def sm2_encrypt(plaintext: bytes,public_key:bytes) -> bytes:
sm2_crypt = sm2.CryptSM2(private_key="",public_key=public_key.hex())
ciphertext = sm2_crypt.encrypt(plaintext)
return ciphertext

def sm3_hash(text:bytes):
hash_value = sm3.sm3_hash(func.bytes_to_list(text))
return hash_value

def read_key_from_file(file_path):
try:
with open(file_path, 'r') as file:
key = file.read().strip()
return key
except FileNotFoundError:
print(f"错误: 文件 {file_path} 未找到。")
except Exception as e:
print(f"错误: 发生了未知错误 {e}。")
return None

# 对由abcd组成的字符串加密的方法
def sm4_encrypt(plaintext:str,sm2_public_key: str,sm4_iv:str):
sm4_key = bytes.fromhex(read_key_from_file("key.txt")) #从文件读取固定的key
# sm4
envelope = EnvelopedData()
plaintext_bytes = plaintext.encode('utf-8')
ciphertext = sm4_cbc_encrypt(plaintext_bytes,sm4_key,bytes.fromhex(sm4_iv))

# sm2
encrypted_key = sm2_encrypt(sm4_key,bytes.fromhex(sm2_public_key))

# sm3
digest = sm3_hash(plaintext_bytes)

envelope['encryptedData'] = EncryptedData()
envelope['encryptedData']['iv'] = univ.OctetString(bytes.fromhex(sm4_iv))
envelope['encryptedData']['cipherText'] = univ.OctetString(ciphertext)

envelope['encryptedKey'] = SM2Cipher()
envelope['encryptedKey']['xCoordinate'] = univ.Integer(int.from_bytes(encrypted_key[:32], 'big'))
envelope['encryptedKey']['yCoordinate'] = univ.Integer(int.from_bytes(encrypted_key[32:64], 'big'))
envelope['encryptedKey']['hash'] = univ.OctetString(encrypted_key[64:96])
envelope['encryptedKey']['cipherText'] = univ.OctetString(encrypted_key[96:])

envelope['digest'] = univ.OctetString(bytes.fromhex(digest))
return encode(envelope).hex()

# 从asn1格式的16进制字符串提取参数
def asn1_parse(asn1_hex_str:str,asn1_spec):
# 将16进制字符串转换为字节
der_bytes = binascii.unhexlify(asn1_hex_str)
# 解码为ASN.1对象
enveloped_data, _ = decode(der_bytes, asn1Spec=asn1_spec)
# sm2
sm2_x = hex(int(enveloped_data['encryptedKey']['xCoordinate']))[2:]
sm2_y = hex(int(enveloped_data['encryptedKey']['yCoordinate']))[2:]
sm2_hash = enveloped_data['encryptedKey']['hash'].asOctets().hex()
sm2_ciphertext = enveloped_data['encryptedKey']['cipherText'].asOctets().hex()

# sm4
sm4_algorithm = str(enveloped_data['encryptedData']['algorithm'])
sm4_iv = enveloped_data['encryptedData']['iv'].asOctets().hex()
sm4_cipherText = enveloped_data['encryptedData']['cipherText'].asOctets().hex()

# sm3
digestAlgorithm = str(enveloped_data['digestAlgorithm'])
digest = enveloped_data['digest'].asOctets().hex()

# 输出提取的值
print("asn1格式的16进制字符串:")
print(f" asn1: {asn1_hex_str}")
print("SM2参数:")
print(f" xCoordinate: {sm2_x}")
print(f" yCoordinate: {sm2_y}")
print(f" hash: {sm2_hash}")
print(f" cipherText: {sm2_ciphertext}")

print("SM4参数:")
print(f" algorithm: {sm4_algorithm}")
print(f" iv: {sm4_iv}")
print(f" cipherText: {sm4_cipherText}")

print("SM3参数:")
print(f" digestAlgorithm: {digestAlgorithm}")
print(f" digest: {digest}")


if __name__ == "__main__":
plaintext = "6163616263626161626461646464636361626263626464626361616164636462636462646461646461626462646361636264616364646462646462626261636261646163626463636262616462646462616362616363646463646361616263646261636164636263646163646161636164646364646261626463636462636162636162646261626163636161616463616261646264616162646162626162626462616363616161636362616461626463616462646261626264626464626262636363636162616261626163616164616462626163636164646161646361626363646462626261636261636164646262646362616263636363626461636164646261636361646463616161626164626461636163636461646164616161616163616164636164646261646163626163636164616162636263616461636261646264626263626264636164646263616164626463626461646364616362626261616262616264616361626264636264616461646163626364626462636161636262636163616261616262626362636463616263616364616363626163636363636262646363616464626461616363646361626162636261636364646362626462616364626462626161616264636162626263626462626264646162626462616261616264626161616363636364616263626461636162616462616363616461646363636261636363616162646164626361616464646463646263646363636164626164646463646361636364616261626261646461646463626161616361626161626362626262636164626463636163626163616163636262646463646162616363616364636164646364626464626164626162636161616263646164636461626161636262646463636462646161636462626264626463646364636362626264616362646462636263616361626262616464636263616464616363646163616262616162626261626261616461636361636164636162626461646264636162646363636263616363646161636464626161616462636464646164616361646264616361626263646264616162636164636462616164646163616461646362626464"
sm2_key = "044f66804d1d30f4499377b96dc8e18faab8300ebddf3eb0fa2065214c260d64c08c6dfe7d9923d6d5baa3a0512a2ede03357c723230ebf77906f82dc1b0fccc1e"
iv = "43d4192f9f74e90543d4192f9f74e905"
asn1_hex_str = sm4_encrypt(bytes.fromhex(plaintext).decode('utf-8'),sm2_key,iv)
asn1_parse(asn1_hex_str,EnvelopedData())

题目实现了一个数字信封,利用 SM4-CBC 对消息的进行加密得到密文,然后用 SM2 对 SM4-CBC 的密钥进行加密,并且给出消息的 SM3 哈希值。

挑战目标是给出一个组明密文,然后通过其恢复一段密文(两组明文都是 abcd 的随机组合)。首先它给的密文是 asn1 编码格式,不过无所谓,反正也给我们解 asn1 的代码了。然后再审计代码,你会发现——好像这个数字信封实现的也没啥问题哎。当时是直接懵逼了,那要咋打,总不能直接爆破明文然后对照 hash 吧。不过注意到给出的明密文很长,而要恢复的明文很短,这时候就脑洞打开了一下,把要恢复的密文的第一块用 ctrl f 搜索了一下,结果发现竟然给定的密文有一块和它一样的。那就好办了,因为是 CBC 形式的而且 iv 也给我们了,所以可以快速计算出第一块的消息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import binascii
from pyasn1.codec.der.decoder import decode
from pyasn1.type import univ, namedtype
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from gmssl import sm3, func, sm2
from pyasn1.codec.der.encoder import encode

class SM2Cipher(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('xCoordinate', univ.Integer()), # -- x 分量
namedtype.NamedType('yCoordinate', univ.Integer()), # -- y 分量
namedtype.NamedType('hash', univ.OctetString()), # --哈希值
namedtype.NamedType('cipherText', univ.OctetString()) # -- SM4密钥密文
)

class EncryptedData(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('algorithm', univ.ObjectIdentifier('1.2.156.10197.1.104.2')), # -- SM4-CBC OID
namedtype.NamedType('iv', univ.OctetString()), # -- SM4-CBC加密使用的初始化向量(IV)
namedtype.NamedType('cipherText', univ.OctetString()) # -- SM4加密的密文
)


class EnvelopedData(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('encryptedKey', SM2Cipher()), # -- 使用SM2公钥加密SM4密钥的密文
namedtype.NamedType('encryptedData', EncryptedData()), # -- 使用SM4密钥对明文加密的密文
namedtype.NamedType('digestAlgorithm', univ.ObjectIdentifier('1.2.156.10197.1.401.1')), # -- SM3算法OID
namedtype.NamedType('digest', univ.OctetString()) # -- 对明文计算的摘要值
)

# 从asn1格式的16进制字符串提取参数
def asn1_parse(asn1_hex_str:str,asn1_spec):
# 将16进制字符串转换为字节
der_bytes = binascii.unhexlify(asn1_hex_str)
# 解码为ASN.1对象
enveloped_data, _ = decode(der_bytes, asn1Spec=asn1_spec)
# sm2
sm2_x = hex(int(enveloped_data['encryptedKey']['xCoordinate']))[2:]
sm2_y = hex(int(enveloped_data['encryptedKey']['yCoordinate']))[2:]
sm2_hash = enveloped_data['encryptedKey']['hash'].asOctets().hex()
sm2_ciphertext = enveloped_data['encryptedKey']['cipherText'].asOctets().hex()

# sm4
sm4_algorithm = str(enveloped_data['encryptedData']['algorithm'])
sm4_iv = enveloped_data['encryptedData']['iv'].asOctets().hex()
sm4_cipherText = enveloped_data['encryptedData']['cipherText'].asOctets().hex()

# sm3
digestAlgorithm = str(enveloped_data['digestAlgorithm'])
digest = enveloped_data['digest'].asOctets().hex()

# 输出提取的值
print("asn1格式的16进制字符串:")
print(f" asn1: {asn1_hex_str}")
print("SM2参数:")
print(f" xCoordinate: {sm2_x}")
print(f" yCoordinate: {sm2_y}")
print(f" hash: {sm2_hash}")
print(f" cipherText: {sm2_ciphertext}")

print("SM4参数:")
print(f" algorithm: {sm4_algorithm}")
print(f" iv: {sm4_iv}")
print(f" cipherText: {sm4_cipherText}")

print("SM3参数:")
print(f" digestAlgorithm: {digestAlgorithm}")
print(f" digest: {digest}")

m1 = '6362646264646264646361636463646261636164626362646264616161636162636261616461646263626362626264636163616264626164616161616361616161636163636361626463616361626263626263646462626261616462646362626162646162646461646264626361646264636164626262626264646464626362626462626364646463636362636264616161636461626363626362616263636264636164636261646262626161636161616461616163616461626461646464616363646462636464646464636462636264636464636162616461646263646264636363616263616164646263646362646161616361646261616161626261626462636261646362626362626462636463616164636263646164636461646364646464616461626463616463636362626464646463636461626264636363636263646463626163636362646264616261636464626164646363626164626264616462626264646463646163646162626364616264646163636362646164616263616461626164636261646361636162616162646161646162636364646162626361646362646361626463616262646362636261616464626162636161616362616464616261626363616464626164616362616262616463626364646461636461636164636261626364646362626263636164626161636264646261636264626164626264636463616263646462646464636461626264626262626263626362636463636362636264646262626162626364616363636264626462636163646361636163616464626361616262646361626261646364646461636164626164646361626264616364626361636163626163636363626464626462646364646364616461616161616361646264626364636364626261636363636361616264636262646262616263636361616361616261616162636163646363646264636364616362626362626463616264646261626164646263646164646264616463646462626261616461636461626262636462646163636461646362616363616163616361626162636462616362636361646363626161636362636361616261636362636463'
c1 = '308203ec30790220467b2389364b2ebd2eadfa624d668c9b0d530b89edbaf9676c1d7db18c7da40502210099f160a4fc540baa3c316e0b28db789f366fc4c84ba1a98e3aaf0806667a82d804204452c9104b87f44ac026d449cb5b5b5f306b2deab5187db7b3cb845659b1714b0410443c9a3f4f7b0083ddd323d6a1f576503082034006082a811ccf550168020410f414bdfbca9d9e902114536a3b9c443204820320d76516cc03294caaa08df866f53ac3ddc8331f5aa82c09d1ae49c41746165293d70f8d2e7578e4a6c59c9a2adb7b446fd7139e6e1d9f4fef7dca09425b574dfd30c24d773c59bcd86384f013439e7b0f61439192f9b7889a72d4fb59d20eaa7dc7191594aa5cdb855410f0b6be69cb5eb5f303a605300d48c8ca0b86549ba2c586009fa7a24e4b71a2c304aa34f9a6cb45d1e5d97e6d768dd63a1bfbe7a975be13585742aed7e3e606450530c05c0cd1c41dea44603f628bc2398cdd706abb66ca964178122b99c879bf1ded48268f662cbb1d06798e19ecb61c5719fd85b4bf72b25226778632e4510009f101a50fc66ef8f12088357ccac4749262f2eb07b6ea6d800ed3897a02d530b0e6f22b378354edddd8bf4a8df07d020986c1f49570853f4875e00fac96b2f8054288ebebb4d86ebfb644f53b1195fc2ff04effe18d419da605ba949ff7a3cc3624fdb71af173a8b8e0c721ff0095f1cfb08d1f236835013811502da499408de1ed0d329027d65b62790043f0b2f67a71f2056ca1c2e431147b145d8817b86a49d08e82c5d710e999078fa8bc1144bb77654a53d91591522540f3ac835ad1df444f94db2c07f223be6db215034ef39c2123690d0457877c40ea91f5662f2efb57cefa4d8b3bb6ce18bed2de4de3c8d53c9bd2f0be84471a88719f74a3e2193915db3c455c597e897ada24e6c3e628e03416a759ed1a7886525569a2b7e7431311504e27add6b43d85e704d364fe6446c29c314e95c498fea6b8123d811480a1add915a34d4ffeda304e0ee8456f842786a385eaac42c0beec3b37d30906a982340a1a9a0151eca11040fd2c467a8a014e2db9ccec96f0d927208de545fddd0cae8b7da3ce113d0ac95fb84ec74ed266ae8676ac662f21bb93034064a043e5969c378e825b3751d6c54e9d8d79a905c942857103564ce7ed5c61c899420d02170f42e41e3193cd5de116701c59043fc42ec596fc8cd05c75ed9fd514c9323ce01143b84080cc2d81ac477c7c2a9ef97ea0b76159fc2ea24e18c8511046f4e500d1653b16b71f0a1028155173a11c55e777dd13c23e67f9fa5e6a8c32162dcf01b09a3c50c106c63d13ce2a9e2fff014665ec490f947706092a811ccf5501831101042024d458eef08943e27b496fd180de68c18f467bee9d3c802bb56d607a364ddc70'
c = '3081e830790220467b2389364b2ebd2eadfa624d668c9b0d530b89edbaf9676c1d7db18c7da40502210099f160a4fc540baa3c316e0b28db789f366fc4c84ba1a98e3aaf0806667a82d804204452c9104b87f44ac026d449cb5b5b5f306b2deab5187db7b3cb845659b1714b0410443c9a3f4f7b0083ddd323d6a1f57650303e06082a811ccf550168020410b63a85e103d362fb6247c19e324e97c4042098fea6b8123d811480a1add915a34d4fdbbfd86515a457e1b20bf4751ea0010706092a811ccf55018311010420aab05fca300811223b3b957bfe33130770fb7a6b55b030a5809c559344f66f79'
# print(asn1_parse(c, EnvelopedData()))

'''
asn1格式的16进制字符串:
asn1: 308203ec30790220467b2389364b2ebd2eadfa624d668c9b0d530b89edbaf9676c1d7db18c7da40502210099f160a4fc540baa3c316e0b28db789f366fc4c84ba1a98e3aaf0806667a82d804204452c9104b87f44ac026d449cb5b5b5f306b2deab5187db7b3cb845659b1714b0410443c9a3f4f7b0083ddd323d6a1f576503082034006082a811ccf550168020410f414bdfbca9d9e902114536a3b9c443204820320d76516cc03294caaa08df866f53ac3ddc8331f5aa82c09d1ae49c41746165293d70f8d2e7578e4a6c59c9a2adb7b446fd7139e6e1d9f4fef7dca09425b574dfd30c24d773c59bcd86384f013439e7b0f61439192f9b7889a72d4fb59d20eaa7dc7191594aa5cdb855410f0b6be69cb5eb5f303a605300d48c8ca0b86549ba2c586009fa7a24e4b71a2c304aa34f9a6cb45d1e5d97e6d768dd63a1bfbe7a975be13585742aed7e3e606450530c05c0cd1c41dea44603f628bc2398cdd706abb66ca964178122b99c879bf1ded48268f662cbb1d06798e19ecb61c5719fd85b4bf72b25226778632e4510009f101a50fc66ef8f12088357ccac4749262f2eb07b6ea6d800ed3897a02d530b0e6f22b378354edddd8bf4a8df07d020986c1f49570853f4875e00fac96b2f8054288ebebb4d86ebfb644f53b1195fc2ff04effe18d419da605ba949ff7a3cc3624fdb71af173a8b8e0c721ff0095f1cfb08d1f236835013811502da499408de1ed0d329027d65b62790043f0b2f67a71f2056ca1c2e431147b145d8817b86a49d08e82c5d710e999078fa8bc1144bb77654a53d91591522540f3ac835ad1df444f94db2c07f223be6db215034ef39c2123690d0457877c40ea91f5662f2efb57cefa4d8b3bb6ce18bed2de4de3c8d53c9bd2f0be84471a88719f74a3e2193915db3c455c597e897ada24e6c3e628e03416a759ed1a7886525569a2b7e7431311504e27add6b43d85e704d364fe6446c29c314e95c498fea6b8123d811480a1add915a34d4ffeda304e0ee8456f842786a385eaac42c0beec3b37d30906a982340a1a9a0151eca11040fd2c467a8a014e2db9ccec96f0d927208de545fddd0cae8b7da3ce113d0ac95fb84ec74ed266ae8676ac662f21bb93034064a043e5969c378e825b3751d6c54e9d8d79a905c942857103564ce7ed5c61c899420d02170f42e41e3193cd5de116701c59043fc42ec596fc8cd05c75ed9fd514c9323ce01143b84080cc2d81ac477c7c2a9ef97ea0b76159fc2ea24e18c8511046f4e500d1653b16b71f0a1028155173a11c55e777dd13c23e67f9fa5e6a8c32162dcf01b09a3c50c106c63d13ce2a9e2fff014665ec490f947706092a811ccf5501831101042024d458eef08943e27b496fd180de68c18f467bee9d3c802bb56d607a364ddc70
SM2参数:
xCoordinate: 467b2389364b2ebd2eadfa624d668c9b0d530b89edbaf9676c1d7db18c7da405
yCoordinate: 99f160a4fc540baa3c316e0b28db789f366fc4c84ba1a98e3aaf0806667a82d8
hash: 4452c9104b87f44ac026d449cb5b5b5f306b2deab5187db7b3cb845659b1714b
cipherText: 443c9a3f4f7b0083ddd323d6a1f57650
SM4参数:
algorithm: 1.2.156.10197.1.104.2
iv: f414bdfbca9d9e902114536a3b9c4432
cipherText: d76516cc03294caaa08df866f53ac3ddc8331f5aa82c09d1ae49c41746165293d70f8d2e7578e4a6c59c9a2adb7b446fd7139e6e1d9f4fef7dca09425b574dfd30c24d773c59bcd86384f013439e7b0f61439192f9b7889a72d4fb59d20eaa7dc7191594aa5cdb855410f0b6be69cb5eb5f303a605300d48c8ca0b86549ba2c586009fa7a24e4b71a2c304aa34f9a6cb45d1e5d97e6d768dd63a1bfbe7a975be13585742aed7e3e606450530c05c0cd1c41dea44603f628bc2398cdd706abb66ca964178122b99c879bf1ded48268f662cbb1d06798e19ecb61c5719fd85b4bf72b25226778632e4510009f101a50fc66ef8f12088357ccac4749262f2eb07b6ea6d800ed3897a02d530b0e6f22b378354edddd8bf4a8df07d020986c1f49570853f4875e00fac96b2f8054288ebebb4d86ebfb644f53b1195fc2ff04effe18d419da605ba949ff7a3cc3624fdb71af173a8b8e0c721ff0095f1cfb08d1f236835013811502da499408de1ed0d329027d65b62790043f0b2f67a71f2056ca1c2e431147b145d8817b86a49d08e82c5d710e999078fa8bc1144bb77654a53d91591522540f3ac835ad1df444f94db2c07f223be6db215034ef39c2123690d0457877c40ea91f5662f2efb57cefa4d8b3bb6ce18bed2de4de3c8d53c9bd2f0be84471a88719f74a3e2193915db3c455c597e897ada24e6c3e628e03416a759ed1a7886525569a2b7e7431311504e27add6b43d85e704d364fe6446c29c314e95c498fea6b8123d811480a1add915a34d4ffeda304e0ee8456f842786a385eaac42c0beec3b37d30906a982340a1a9a0151eca11040fd2c467a8a014e2db9ccec96f0d927208de545fddd0cae8b7da3ce113d0ac95fb84ec74ed266ae8676ac662f21bb93034064a043e5969c378e825b3751d6c54e9d8d79a905c942857103564ce7ed5c61c899420d02170f42e41e3193cd5de116701c59043fc42ec596fc8cd05c75ed9fd514c9323ce01143b84080cc2d81ac477c7c2a9ef97ea0b76159fc2ea24e18c8511046f4e500d1653b16b71f0a1028155173a11c55e777dd13c23e67f9fa5e6a8c32162dcf01b09a3c50c106c63d13ce2a9e2fff014665ec490f9477
SM3参数:
digestAlgorithm: 1.2.156.10197.1.401.1
digest: 24d458eef08943e27b496fd180de68c18f467bee9d3c802bb56d607a364ddc70
None
'''

'''
asn1格式的16进制字符串:
asn1: 3081e830790220467b2389364b2ebd2eadfa624d668c9b0d530b89edbaf9676c1d7db18c7da40502210099f160a4fc540baa3c316e0b28db789f366fc4c84ba1a98e3aaf0806667a82d804204452c9104b87f44ac026d449cb5b5b5f306b2deab5187db7b3cb845659b1714b0410443c9a3f4f7b0083ddd323d6a1f57650303e06082a811ccf550168020410b63a85e103d362fb6247c19e324e97c4042098fea6b8123d811480a1add915a34d4fdbbfd86515a457e1b20bf4751ea0010706092a811ccf55018311010420aab05fca300811223b3b957bfe33130770fb7a6b55b030a5809c559344f66f79
SM2参数:
xCoordinate: 467b2389364b2ebd2eadfa624d668c9b0d530b89edbaf9676c1d7db18c7da405
yCoordinate: 99f160a4fc540baa3c316e0b28db789f366fc4c84ba1a98e3aaf0806667a82d8
hash: 4452c9104b87f44ac026d449cb5b5b5f306b2deab5187db7b3cb845659b1714b
cipherText: 443c9a3f4f7b0083ddd323d6a1f57650
SM4参数:
algorithm: 1.2.156.10197.1.104.2
iv: b63a85e103d362fb6247c19e324e97c4
cipherText: 98fea6b8123d811480a1add915a34d4fdbbfd86515a457e1b20bf4751ea00107
SM3参数:
digestAlgorithm: 1.2.156.10197.1.401.1
digest: aab05fca300811223b3b957bfe33130770fb7a6b55b030a5809c559344f66f79
None
'''
def xor(a, b):
return bytes([i ^ j for i, j in zip(a, b)])


m1 = '6362646264646264646361636463646261636164626362646264616161636162636261616461646263626362626264636163616264626164616161616361616161636163636361626463616361626263626263646462626261616462646362626162646162646461646264626361646264636164626262626264646464626362626462626364646463636362636264616161636461626363626362616263636264636164636261646262626161636161616461616163616461626461646464616363646462636464646464636462636264636464636162616461646263646264636363616263616164646263646362646161616361646261616161626261626462636261646362626362626462636463616164636263646164636461646364646464616461626463616463636362626464646463636461626264636363636263646463626163636362646264616261636464626164646363626164626264616462626264646463646163646162626364616264646163636362646164616263616461626164636261646361636162616162646161646162636364646162626361646362646361626463616262646362636261616464626162636161616362616464616261626363616464626164616362616262616463626364646461636461636164636261626364646362626263636164626161636264646261636264626164626264636463616263646462646464636461626264626262626263626362636463636362636264646262626162626364616363636264626462636163646361636163616464626361616262646361626261646364646461636164626164646361626264616364626361636163626163636363626464626462646364646364616461616161616361646264626364636364626261636363636361616264636262646262616263636361616361616261616162636163646363646264636364616362626362626463616264646261626164646263646164646264616463646462626261616461636461626262636462646163636461646362616363616163616361626162636462616362636361646363626161636362636361616261636362636463'
iv1 = bytes.fromhex('f414bdfbca9d9e902114536a3b9c4432')
ct1 = 'd76516cc03294caaa08df866f53ac3ddc8331f5aa82c09d1ae49c41746165293d70f8d2e7578e4a6c59c9a2adb7b446fd7139e6e1d9f4fef7dca09425b574dfd30c24d773c59bcd86384f013439e7b0f61439192f9b7889a72d4fb59d20eaa7dc7191594aa5cdb855410f0b6be69cb5eb5f303a605300d48c8ca0b86549ba2c586009fa7a24e4b71a2c304aa34f9a6cb45d1e5d97e6d768dd63a1bfbe7a975be13585742aed7e3e606450530c05c0cd1c41dea44603f628bc2398cdd706abb66ca964178122b99c879bf1ded48268f662cbb1d06798e19ecb61c5719fd85b4bf72b25226778632e4510009f101a50fc66ef8f12088357ccac4749262f2eb07b6ea6d800ed3897a02d530b0e6f22b378354edddd8bf4a8df07d020986c1f49570853f4875e00fac96b2f8054288ebebb4d86ebfb644f53b1195fc2ff04effe18d419da605ba949ff7a3cc3624fdb71af173a8b8e0c721ff0095f1cfb08d1f236835013811502da499408de1ed0d329027d65b62790043f0b2f67a71f2056ca1c2e431147b145d8817b86a49d08e82c5d710e999078fa8bc1144bb77654a53d91591522540f3ac835ad1df444f94db2c07f223be6db215034ef39c2123690d0457877c40ea91f5662f2efb57cefa4d8b3bb6ce18bed2de4de3c8d53c9bd2f0be84471a88719f74a3e2193915db3c455c597e897ada24e6c3e628e03416a759ed1a7886525569a2b7e7431311504e27add6b43d85e704d364fe6446c29c314e95c498fea6b8123d811480a1add915a34d4ffeda304e0ee8456f842786a385eaac42c0beec3b37d30906a982340a1a9a0151eca11040fd2c467a8a014e2db9ccec96f0d927208de545fddd0cae8b7da3ce113d0ac95fb84ec74ed266ae8676ac662f21bb93034064a043e5969c378e825b3751d6c54e9d8d79a905c942857103564ce7ed5c61c899420d02170f42e41e3193cd5de116701c59043fc42ec596fc8cd05c75ed9fd514c9323ce01143b84080cc2d81ac477c7c2a9ef97ea0b76159fc2ea24e18c8511046f4e500d1653b16b71f0a1028155173a11c55e777dd13c23e67f9fa5e6a8c32162dcf01b09a3c50c106c63d13ce2a9e2fff014665ec490f9477'
iv2 = bytes.fromhex('b63a85e103d362fb6247c19e324e97c4')
ct2 = '98fea6b8123d811480a1add915a34d4fdbbfd86515a457e1b20bf4751ea00107'
d = 'aab05fca300811223b3b957bfe33130770fb7a6b55b030a5809c559344f66f79'
t = ct1.index('98fea6b8123d811480a1add915a34d4f')

assert ct1[t:t+32] == ct2[:32]
mmm = bytes.fromhex(m1[t:t+32])
m2 = xor(xor(mmm, bytes.fromhex(ct1[t-32:t])), iv2)
print(m2)

然后对于第二块,因为长度也不长而且就是明文空间也很小,那就爆吧,时间复杂度也才 2322^{32}。但是我用 python 实现的话要 300h,那就让队友用 go 给我写了一个,最后用了 8min 就出了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package main

import (
"fmt"
"sync"
"time"

"github.com/tjfoc/gmsm/sm3"
)

var char = []byte("abcd")

func index_to_char(index uint64) []byte {
res := []byte{}
for {
if len(res) == 15 {
return append(res, char[index])
}
res = append(res, char[index%4])
index /= 4
}
}

var char_set = []byte("adcddbbadcacabad")
const tar_hex = "aab05fca300811223b3b957bfe33130770fb7a6b55b030a5809c559344f66f79"

func main() {
var j uint64
wg := &sync.WaitGroup{}
wg.Add(256)
time_start := time.Now()
defer wg.Wait()
for j = 0; j <= 0xff; j++ {
go func(j uint64) {
defer wg.Done()
var i uint64
for i = 0; i <= 0xffffff; i++ {
data := append(char_set, index_to_char((j << 24) + i)...)
// fmt.Println(data)
hash := sm3.New()
hash.Write(data)
sum := hash.Sum(nil)
sum_hex := fmt.Sprintf("%x", sum)
// fmt.Printf("j: %d, i: %d, sum_hex: %s\n", j, i, sum_hex)
if sum_hex == tar_hex {
fmt.Println(string(data))
return
}
}
}(j)
}
wg.Wait()
time_end := time.Now()
fmt.Printf("Time taken: %v\n", time_end.Sub(time_start))
}

初始谜题2

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
import binascii
from datetime import datetime
from pyasn1.type import univ, namedtype
from pyasn1.codec.der.encoder import encode
from pyasn1.codec.der.decoder import decode
from gmssl import sm2
from pyasn1.codec.der import decoder, encoder
from pyasn1_modules import rfc2459
from gmssl.sm2 import CryptSM2
from pyasn1.type.useful import GeneralizedTime
from pyasn1.type.univ import Sequence
from pyasn1.type import useful


class ECPrimeFieldConfig(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('fieldType', univ.ObjectIdentifier('1.2.840.10045.1.1')), # Prime field OID
namedtype.NamedType('prime', univ.Integer()), # Prime number p
)


class ECCurveParameters(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('coefficientA', univ.OctetString()), # Curve coefficient a
namedtype.NamedType('coefficientB', univ.OctetString()), # Curve coefficient b
)


class ECDomainParameters(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('version', univ.Integer(1)), # Version number (1)
namedtype.NamedType('fieldParameters', ECPrimeFieldConfig()), # Field parameters 包含参数oid,p
namedtype.NamedType('curveParameters', ECCurveParameters()), # Curve parameters 包含参数a,b
namedtype.NamedType('basePoint', univ.OctetString()), # Base point G 基点
namedtype.NamedType('order', univ.Integer()), # Order n of base point 参数n
namedtype.NamedType('cofactor', univ.Integer(1)), # Cofactor 余因子 固定值为1
)


class SM2SignatureValue(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('r', univ.Integer()), # First part of signature
namedtype.NamedType('s', univ.Integer()), # Second part of signature
)


class SM2SignedData(univ.Sequence):
componentType = namedtype.NamedTypes(
# version
namedtype.NamedType('version', univ.Integer()),
# 哈希算法 OID(SM3)
namedtype.NamedType('digestAlgorithms', univ.ObjectIdentifier()),
# 签名值 r, s
namedtype.NamedType('sm2Signature', SM2SignatureValue()),
# 曲线参数
namedtype.NamedType('ecDomainParameters', ECDomainParameters()),
# 证书
namedtype.NamedType('certificate', univ.OctetString()),
# 签名时间
namedtype.NamedType('timestamp', GeneralizedTime()),
)


# 输入值全部为16进制字符串,g为x,y坐标的16进制字符串进行拼接
# p,a,b,n,g对应曲线参数;r,s为签名值的两部分
def asn1_package(version, oid, signature, curve_params, cert_hex, time_stamp):
sm2_signed_data = SM2SignedData()
# version
sm2_signed_data['version'] = version
# 哈希算法 OID(SM3)
sm2_signed_data['digestAlgorithms'] = oid
# 签名值 r, s
sm2_signed_data["sm2Signature"] = SM2SignatureValue()
sm2_signed_data["sm2Signature"]['r'] = int(signature[:64], 16)
sm2_signed_data["sm2Signature"]['s'] = int(signature[64:], 16)
# 曲线参数
sm2_signed_data["ecDomainParameters"] = ECDomainParameters()
sm2_signed_data["ecDomainParameters"]["fieldParameters"] = ECPrimeFieldConfig()
sm2_signed_data["ecDomainParameters"]["fieldParameters"]["prime"] = int(curve_params['p'], 16)
sm2_signed_data["ecDomainParameters"]["curveParameters"] = ECCurveParameters()
sm2_signed_data["ecDomainParameters"]["curveParameters"]["coefficientA"] = univ.OctetString(
bytes.fromhex(curve_params['a']))
sm2_signed_data["ecDomainParameters"]["curveParameters"]["coefficientB"] = univ.OctetString(
bytes.fromhex(curve_params['b']))
sm2_signed_data["ecDomainParameters"]['basePoint'] = univ.OctetString(bytes.fromhex('04' + curve_params['g']))
sm2_signed_data["ecDomainParameters"]['order'] = int(curve_params['n'], 16)
# 证书
sm2_signed_data["certificate"] = univ.OctetString(bytes.fromhex(cert_hex))
# 时间
dt = datetime.strptime(time_stamp, "%Y-%m-%d %H:%M:%S")
asn1_time_str = dt.strftime("%Y%m%d%H%M%SZ")
sm2_signed_data["timestamp"] = GeneralizedTime(asn1_time_str)
return encode(sm2_signed_data).hex()


class Sm2CertVerifier:
def __init__(self, cert_hex: str):
ca_pubkey = "8E1860588D9900C16BD19A0FE0A5ACC600224DBD794FFD34179E03698D52421F46E6D8C6E8AADE512C7B543395AC39C76384726C7F8BA537ABCA0C129ECD9882"
self.sm2_crypt = sm2.CryptSM2(public_key=ca_pubkey, private_key=None)
self.cert_tbs, self.signature_bytes, self.cert = self.parse_cert(bytes.fromhex(cert_hex))

@staticmethod
def parse_cert(cert_der_bytes: bytes):
cert, _ = decoder.decode(cert_der_bytes, asn1Spec=rfc2459.Certificate())
tbs = cert.getComponentByName('tbsCertificate')
signature_bytes = cert.getComponentByName('signatureValue').asOctets()
return tbs, signature_bytes, cert

# 获取签名值
def decode_rs_from_der(self, signature: bytes) -> bytes:
seq, _ = decode(signature, asn1Spec=Sequence())
r = int(seq[0])
s = int(seq[1])
r_bytes = r.to_bytes(32, byteorder='big')
s_bytes = s.to_bytes(32, byteorder='big')
return r_bytes + s_bytes

def verify_signature(self, signature: bytes, tbs: str):
inter_cert_tbs_der = encoder.encode(tbs)
inter_signature = self.decode_rs_from_der(signature)
# 验证签名(tbs_der必须完整,签名必须64字节)
return self.sm2_crypt.verify_with_sm3(inter_signature.hex(), inter_cert_tbs_der)

def verify_certificate_expiration_date(self, tbs):
validity = tbs.getComponentByName('validity')
not_before = validity.getComponentByName('notBefore').getComponent()
not_after = validity.getComponentByName('notAfter').getComponent()

# 处理 UTCTime 和 GeneralizedTime 两种类型
if isinstance(not_before, useful.UTCTime):
not_before_time = datetime.strptime(str(not_before), "%y%m%d%H%M%SZ")
elif isinstance(not_before, useful.GeneralizedTime):
not_before_time = datetime.strptime(str(not_before), "%Y%m%d%H%M%SZ")
else:
raise ValueError("Unsupported notBefore time format")

if isinstance(not_after, useful.UTCTime):
not_after_time = datetime.strptime(str(not_after), "%y%m%d%H%M%SZ")
elif isinstance(not_after, useful.GeneralizedTime):
not_after_time = datetime.strptime(str(not_after), "%Y%m%d%H%M%SZ")
else:
raise ValueError("Unsupported notAfter time format")

now = datetime.now()
return not_before_time <= now <= not_after_time

def verify(self):
# 验证中间证书有效期
if not self.verify_certificate_expiration_date(self.cert_tbs):
print("证书已过期或尚未生效")
return False
# 验证中间证书签名
if not self.verify_signature(self.signature_bytes, self.cert_tbs):
print("证书验证未通过")
return False
return True


class SM2Config:
# sm2参数初始化
def __init__(self, asn1_str):
self.sm2_signed_data,asn1_acess = self.hex_to_asn1(asn1_str, SM2SignedData())
if len(asn1_acess) != 0:
raise ValueError("asn1长度有问题")
cert_hex = self.get_hex_value(self.sm2_signed_data['certificate'])
sm2_cert_verifier = Sm2CertVerifier(cert_hex)
valid = sm2_cert_verifier.verify()
if not valid:
raise TypeError("证书验证不通过")
g = self.get_hex_value(self.sm2_signed_data['ecDomainParameters']['basePoint'])
g = g[2:] if g.startswith("04") else g
self.ecc_table = {
'n': self.get_hex_value(self.sm2_signed_data['ecDomainParameters']['order']),
'p': self.get_hex_value(self.sm2_signed_data['ecDomainParameters']['fieldParameters']['prime']),
'g': g,
'a': self.get_hex_value(self.sm2_signed_data['ecDomainParameters']['curveParameters']['coefficientA']),
'b': self.get_hex_value(self.sm2_signed_data['ecDomainParameters']['curveParameters']['coefficientB']),
}
public_key = self.extract_public_key(sm2_cert_verifier.cert_tbs)
self.sm2_crypt = CryptSM2(
private_key="",
public_key=public_key,
ecc_table=self.ecc_table
)
self.sign = (int(self.sm2_signed_data['sm2Signature']['r']).to_bytes(32, 'big').hex().upper() +
int(self.sm2_signed_data['sm2Signature']['s']).to_bytes(32, 'big').hex().upper())

@staticmethod
def hex_to_asn1(hex_str, asn1_spec):
"""
将16进制字符串转换回ASN.1对象
:param hex_str: 16进制字符串
:param asn1_spec: ASN.1结构定义
:return: ASN.1对象
"""
# 将16进制字符串转换为字节
der_bytes = binascii.unhexlify(hex_str)

# 解码为ASN.1对象
asn1_object, excess = decode(der_bytes, asn1Spec=asn1_spec)

return asn1_object,excess

@staticmethod
def get_hex_value(value):
"""通用转换函数:将 ASN.1 值转换为 16 进制字符串(大写,无前缀)"""
if isinstance(value, univ.Integer):
return format(int(value), 'X') # Integer -> 直接转十六进制
elif isinstance(value, univ.OctetString):
return value.asOctets().hex().upper() # OctetString -> 字节转十六进制
else:
raise TypeError(f"Unsupported type: {type(value)}")

@staticmethod
def extract_public_key(tbs):
spki = tbs.getComponentByName('subjectPublicKeyInfo')
public_key_bitstring = spki.getComponentByName('subjectPublicKey')
# 提取位串内容(包含开头的 0x04)
pubkey_bytes = bytearray(public_key_bitstring.asOctets())
# 转成十六进制字符串
return pubkey_bytes.hex()

def verify_misc(self):
if (int(self.sm2_signed_data['version']) != 1 or
str(self.sm2_signed_data['digestAlgorithms']) != '1.2.156.10197.1.401.1' or
str(self.sm2_signed_data['timestamp']) != "20250520101000Z"):
return False
return True

# sm2验签
def verify(self, data):
valid = self.verify_misc()
if not valid:
return valid
valid = self.sm2_crypt.verify_with_sm3(self.sign, data)
return valid


# 通过该函数可以产生一个合法的SM2SignedData
def generateSM2SignedDataExample():
# 版本
version = 1
# 哈希算法oid
oid = '1.2.156.10197.1.401.1'
# 签名值r, s
signature = '6f8eaff551d0f3fa6de74b75b33e1e58f9fdb4dc58e61c82e11e717ffcf168c4db3d5a90ff3625d12b8b658f8dbab34340c278b412b3aff25489e7feb1c75598'
r = signature[:64]
s = signature[64:]
# 曲线参数
curve_params = {
"n": 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123',
"p": 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF',
"g": '32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0',
"a": 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC',
"b": '28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93',
}
# 证书
cert_hex = '3082017C30820122A003020102020D00947C8427D3E849B48A7E5136300A06082A811CCF550183753036310B300906035504061302434E31133011060355040A130A5368616E674D6942656931123010060355040313095368616E674D694341301E170D3235303532303035353330365A170D3330303531393035353330365A304D310B300906035504061302434E3110300E060355040A1307496E746572434131173015060355040B130E5368616E674D6942656932303235311330110603550403130A7368616E676D696265693059301306072A8648CE3D020106082A811CCF5501822D03420004CECC0005AED684A1E7E39C316E7F3F39BDD0490936BC0E1AFDDC1B9627A05B4418809E5327746EE1977913F036EF0A9A255C27D73C00E45D0BB205B34D2C80D4300A06082A811CCF5501837503480030450220360779CBF5AA6E5E9CC073D95E22C52C09E81CFC06A3916559063A3C8C1DFDE6022100ED0E5E5E51F3894A3EAC11F247739D9F6A88C961D89F68337972BC3CC6BB6706' # 证书16进制格式
# 时间
time_stamp = '2025-05-20 10:10:00'

# asn1封装
asn1_package_hex = asn1_package(version, oid, signature, curve_params, cert_hex, time_stamp)
return(asn1_package_hex)


if __name__ == '__main__':
# 验签
data = b"Hello, CryptoCup!"
asn1_package_hex = generateSM2SignedDataExample()
sm2_config = SM2Config(asn1_package_hex)
result = sm2_config.verify(data)
print(result)

SM2SignedData.txt

1
308202CD02010106092A811CCF5501831101304502206F8EAFF551D0F3FA6DE74B75B33E1E58F9FDB4DC58E61C82E11E717FFCF168C4022100DB3D5A90FF3625D12B8B658F8DBAB34340C278B412B3AFF25489E7FEB1C755983081E0020101302C06072A8648CE3D0101022100FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF30440420FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC042028E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E9304410432C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0022100FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123020101048201803082017C30820122A003020102020D00947C8427D3E849B48A7E5136300A06082A811CCF550183753036310B300906035504061302434E31133011060355040A130A5368616E674D6942656931123010060355040313095368616E674D694341301E170D3235303532303035353330365A170D3330303531393035353330365A304D310B300906035504061302434E3110300E060355040A1307496E746572434131173015060355040B130E5368616E674D6942656932303235311330110603550403130A7368616E676D696265693059301306072A8648CE3D020106082A811CCF5501822D03420004CECC0005AED684A1E7E39C316E7F3F39BDD0490936BC0E1AFDDC1B9627A05B4418809E5327746EE1977913F036EF0A9A255C27D73C00E45D0BB205B34D2C80D4300A06082A811CCF5501837503480030450220360779CBF5AA6E5E9CC073D95E22C52C09E81CFC06A3916559063A3C8C1DFDE6022100ED0E5E5E51F3894A3EAC11F247739D9F6A88C961D89F68337972BC3CC6BB6706180F32303235303532303130313030305A

题目代码需要生成一个指定数据的签名,而这个公钥是在证书中,证书链验证时公钥固定,所以无法修改证书,但是题目额外让我们自己指定曲线参数。

直接对给定的证书里面的公钥Q,可以用sagemath计算出 255G=Q255G'=Q,然后把基点替换为 GG' 即可通过验签。

首先计算 GG'

1
2
3
4
5
6
7
8
9
pub = "cecc0005aed684a1e7e39c316e7f3f39bdd0490936bc0e1afddc1b9627a05b4418809e5327746ee1977913f036ef0a9a255c27d73c00e45d0bb205b34d2c80d4"

x,y = int(pub[:64],16), int(pub[64:], 16)
P = E(x,y)
G = P * inverse_mod(0xff,n)
G
x = 36937047021928959731165221160519944594794112257956092575886609274654427603612
y = 104673289685811405702677634451265453400814492624193002291684496503421528948368
hex(x)[2:].zfill(64) + hex(y)[2:].zfill(64)

然后调用gmssl库生成签名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from gmssl.sm2 import CryptSM2

curve_params = {
"n": 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123',
"p": 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF',
"g": '51a9a0b4050aaa36ea04d9004d0e477dad7dfb19021147a168969748cd46629ce76afb832729a051f24993c9c201921933b29657a070fd6c537fca04af0fbe90',
"a": 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC',
"b": '28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93',
}

sm2_crypt = CryptSM2(
private_key="ff",
public_key="cecc0005aed684a1e7e39c316e7f3f39bdd0490936bc0e1afddc1b9627a05b4418809e5327746ee1977913f036ef0a9a255c27d73c00e45d0bb205b34d2c80d4",
ecc_table=curve_params
)

msg = b'EUWJSFTFHGQEQVRXZHJYKPUXDPUMRDQQ'
sig = sm2_crypt.sign_with_sm3(msg)
print(sig)

初始谜题3

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
from typing import List, Callable
from hashlib import sha256

def hex_to_32byte_chunks(hex_str):
# 确保十六进制字符串长度是64的倍数(因为32字节 = 64个十六进制字符)
if len(hex_str) % 64 != 0:
raise ValueError("十六进制字符串长度必须是64的倍数")

# 每64个字符分割一次,并转换为字节
return [bytes.fromhex(hex_str[i:i + 64]) for i in range(0, len(hex_str), 64)]

def openssl_sha256(message: bytes) -> bytes:
return sha256(message).digest()

class WOTSPLUS:
def __init__(
self,
w: int = 16, # Winternitz 参数,控制空间与时间的复杂度
hashfunction: Callable = openssl_sha256, # 哈希函数
digestsize: int = 256, # 摘要大小,单位为比特
pubkey: List[bytes] = None,
) -> None:
self.w = w
if not (2 <= w <= (1 << digestsize)):
raise ValueError("规则错误:2 <= w <= 2^digestsize")
# 消息摘要所需的密钥数量(默认8个)
self.msg_key_count = 8
# 校验和密钥数量
self.cs_key_count = 0
# 总密钥数量 = 消息密钥 + 校验和密钥
self.key_count = self.msg_key_count + self.cs_key_count
self.hashfunction = hashfunction
self.digestsize = digestsize
self.pubkey = pubkey

@staticmethod
def number_to_base(num: int, base: int) -> List[int]:
if num == 0:
return [0] # 如果数字是 0,直接返回 0

digits = [] # 存储转换后的数字位
while num:
digits.append(int(num % base)) # 获取当前数字在目标进制下的个位,并添加到结果列表
num //= base # 对数字进行整除,处理下一位

return digits[::-1] # 返回按顺序排列的结果

def _chain(self, value: bytes, startidx: int, endidx: int) -> bytes:
for i in range(startidx, endidx):
value = self.hashfunction(value) # 每次迭代对当前哈希值进行哈希操作

return value

def get_signature_base_message(self, msghash: bytes) -> List[int]:
# 将消息哈希从字节转换为整数
msgnum = int.from_bytes(msghash, "big")

# 将消息的数字表示转换为特定进制下的比特组表示
msg_to_sign = self.number_to_base(msgnum, self.w)

# 校验消息比特组的数量是否符合预期
if len(msg_to_sign) > self.msg_key_count:
err = (
"The fingerprint of the message could not be split into the"
+ " expected amount of bitgroups. This is most likely "
+ "because the digestsize specified does not match to the "
+ " real digestsize of the specified hashfunction Excepted:"
+ " {} bitgroups\nGot: {} bitgroups"
)
raise IndexError(err.format(self.msg_key_count, len(msg_to_sign)))

return msg_to_sign

def get_pubkey_from_signature(
self, digest: bytes, signature: List[bytes]
) -> List[bytes]:
msg_to_verify = self.get_signature_base_message(digest)

result = []
for idx, val in enumerate(msg_to_verify):
sig_part = signature[idx]
chained_val = self._chain(sig_part, val, self.w - 1)
result.append(chained_val)
return result

def verify(self, digest: bytes, signature: List[bytes]) -> bool:
pubkey = self.get_pubkey_from_signature(digest, signature)
return True if pubkey == self.pubkey else False

if __name__ == "__main__":
pubkey_hex = "5057432973dc856a7a00272d83ea1c14de52b5eb3ba8b70b373db8204eb2f902450e38dbade5e9b8c2c3f8258edc4b7e8101e94ac86e4b3cba92ddf3d5de2a2b454c067a995060d1664669b45974b15b3423cec342024fe9ccd4936670ec3abaae4f6b97279bd8eb26463a8cb3112e6dcbf6301e4142b9cdc4adfb644c7b114af4f0cf8f80e22c3975ba477dc4769c3ef67ffdf2090735d81d07bc2e6235af1ee41ef332215422d31208c2bc2163d6690bd32f4926b2858ca41c12eec88c0a300571901a3f674288e4a623220fb6b70e558d9819d2f23da6d897278f4056c346d7f729f5f70805ad4e5bd25cfa502c0625ac02185e014cf36db4ebcdb3ed1a38"
pubkey_list_bytes = hex_to_32byte_chunks(pubkey_hex)
wots = WOTSPLUS(pubkey = pubkey_list_bytes)
digest_hex = "84ffb82e"
signature_hex = "25d5a0e650d683506bfe9d2eca6a3a99b547a4b99398622f6666ce10131e971b6bd36841c9074fe9b4de2900ebe3fadb3202a173be486da6cf8f3d8c699c95c3454c067a995060d1664669b45974b15b3423cec342024fe9ccd4936670ec3abaae4f6b97279bd8eb26463a8cb3112e6dcbf6301e4142b9cdc4adfb644c7b114a4966398a789b56bdb09ea195925e7e8cde372305d244604c48db08f08a6e8a38951030deb25a7aaf1c07152a302ebc07d5d0893b5e9a5953f3b8500179d138b9aa90c0aaacea0c23d22a25a86c0b747c561b480175b548fcb1f4ad1153413bc74d9c049d43ffe18ceee31e5be8bdb9968103ef32fb4054a4a23c400bbfe0d89f"
digest_bytes = bytes.fromhex(digest_hex)
signature = hex_to_32byte_chunks(signature_hex)
valid = wots.verify(digest_bytes,signature)
print(valid)

这个最简单了,它实现了一个 OTS 的验签函数,但是没有给出签名函数。目标是给定一组签名和公钥要求你伪造一组签名可以通过。

我们都知道,OTS 的私钥是不能重用的,这里公钥一样那私钥也肯定一样的。一个简单的想法,我直接令摘要为 ffffffff 那么对于每一组其的签名都是对私钥进行 255 次 hash 操作。但是公钥也是对私钥进行 255 次摘要,那就说明了如果摘要是 ffffffff,那么签名就是公钥了。所以这题其实只给公钥也可以伪造的。

密码系统

这里总共 6 个flag,其中 flag1 做完了才可以做后面的 2、3、4、5,然后全部做完就会送一个 flag6

1-TSP服务器登录

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package service

import (
"crypto/ecdsa"
"encoding/asn1"
"encoding/hex"
"errors"
"fmt"
"github.com/tjfoc/gmsm/sm2"
"math/big"
)

func (s *LoginService) Login(username, authInfo, certStr string) (token string, err error) {
// 读取用户证书
cert, err := s.CertService.LoadCertificate(certStr)
if err != nil {
return "", err
}

if err := s.CertService.ValidateCertificate(cert, RootCert); err != nil {
return "", err
}

// 校验用户名
if cert.Subject.CommonName != username {
err = errors.New("username is not valid")
}

// 判断是否挑战成功(随机字符串的签名能否用证书中的公钥验签过)
ecdsaPubKey, ok := cert.PublicKey.(*ecdsa.PublicKey)
if !ok {
return "", errors.New("public key in cert is not sm2")
}
sm2PubKey := sm2.PublicKey{
Curve: ecdsaPubKey.Curve,
X: ecdsaPubKey.X,
Y: ecdsaPubKey.Y,
}

// 从 authInfo 中提取 randomStr 和 signature
if len(authInfo) != 256 {
return "", errors.New("鉴别信息格式有误")
}
randomStr := authInfo[0:128]
signature := authInfo[128:]

_, err = ValidateSignature(randomStr, signature, &sm2PubKey)
if err != nil {
return "", err
}
return s.generateToken(username)
}

// 验证签名
func ValidateSignature(messageHex, signatureHex string, publicKey *sm2.PublicKey) (bool, error) {
msg, err := hex.DecodeString(messageHex)
if err != nil {
return false, errors.New("挑战值格式有误")
}

if len(signatureHex) != 128 {
return false, errors.New("签名值格式有误")
}

r, ok := big.NewInt(0).SetString(signatureHex[:64], 16)
if !ok {
return false, errors.New("签名值格式有误")
}
s, ok := big.NewInt(0).SetString(signatureHex[64:], 16)
if !ok {
return false, errors.New("签名值格式有误")
}

signature, err := asn1.Marshal(struct{ R, S *big.Int }{r, s})
if err != nil {
return false, errors.New("签名值格式有误")
}

isValid := publicKey.Verify(msg, signature)
if isValid {
return true, nil
} else {
return false, fmt.Errorf("签名无效")
}
}

使用智能密码钥匙登录TSP服务器的历史抓包数据.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
智能密码钥匙抓包数据分析备忘:如果一条指令未全部发送完数据,下一条指令继续发送数据时第一个字节为标志位。

Bus Hound 6.00 capture on Windows Vista (x64). Complements of www.perisoft.net

Device - Device ID (followed by the endpoint for USB devices)
(29) USB Input Device
Phase - Phase Type
CTL USB control transfer
IN Data in transfer
OUT Data out transfer
Data - Hex dump of the data transferred
Descr - Description of the phase
Cmd... - Position in the captured data


Device Phase Data Description Cmd.Phase.Ofs(rep)
------ ----- ------------------------------------------------------------------------------------------------------ -------------------------------- ------------------
29.0 CTL 21 0a 00 00 00 00 00 00 SET IDLE 1.1.0(2)
29.1 OUT de fe 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0a 00 00 01 12 00 07 c0 0a 00 80 00 00 80 00 ................................ 3.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 3.1.32
29.1 IN bf aa aa 85 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 12 00 82 67 6d 33 30 30 30 00 00 ........................gm3000.. 4.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 4.1.32
29.1 IN 3f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ?............................... 5.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 5.1.32
29.1 IN 5b 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90 00 00 00 00 00 [............................... 6.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 6.1.32
29.1 OUT de fe 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0a 00 00 02 12 00 07 80 04 01 00 00 00 00 00 ................................ 7.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 7.1.32
29.1 IN bf aa aa 49 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 12 00 46 47 4d 33 30 30 30 00 00 ...I...................FGM3000.. 8.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 37 39 37 33 33 30 41 31 ........................797330A1 8.1.32
29.1 IN 5e 35 30 46 33 36 41 45 45 38 39 33 39 43 42 36 41 36 39 36 39 41 30 44 00 05 00 02 17 90 00 00 ^50F36AEE8939CB6A6969A0D........ 9.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 9.1.32
29.1 OUT de fe 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0a 00 00 03 12 00 07 80 04 00 00 00 00 00 00 ................................ 10.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 10.1.32
29.1 IN bf aa aa 25 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 12 01 22 01 00 01 00 4c 6f 6e 67 ...%..................."....Long 11.1.0
6d 61 69 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 mai............................. 11.1.32
29.1 IN 3f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 4c 6f 6e ?............................Lon 12.1.0
67 6d 61 69 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 gmai............................ 12.1.32
29.1 IN 3f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 47 4d ?.............................GM 13.1.0
33 30 30 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 37 39 3000..........................79 13.1.32
29.1 IN 3f 37 33 33 30 41 31 35 30 46 33 36 41 45 45 38 39 33 39 43 42 36 41 36 39 36 39 41 30 44 00 05 ?7330A150F36AEE8939CB6A6969A0D.. 14.1.0
00 02 17 07 07 00 00 00 07 03 00 07 00 00 00 01 04 00 00 00 02 00 00 00 01 ee 80 04 20 00 01 00 ............................ ... 14.1.32
29.1 IN 7d 02 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 15.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90 00 00 00 ................................ 15.1.32
29.1 OUT de fe 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0a 00 00 04 12 00 07 80 22 00 00 00 00 00 00 ........................."...... 16.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 16.1.32
29.1 IN e4 aa aa 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 12 00 0d 47 4d 33 30 30 30 52 53 ........................GM3000RS 17.1.0
41 00 00 90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 A............................... 17.1.32
29.1 OUT e9 fe 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 15 00 00 05 12 00 12 80 26 00 00 00 00 09 47 .........................&.....G 18.1.0
4d 33 30 30 30 52 53 41 00 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 M3000RSA........................ 18.1.32
29.1 IN e3 aa aa 0f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 12 00 0c ff 00 00 00 00 00 00 00 ................................ 19.1.0
10 00 90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 19.1.32
29.1 OUT de fe 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0a 00 00 06 12 00 07 80 50 00 00 00 00 08 00 .........................P...... 20.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 20.1.32
29.1 IN e1 aa aa 0d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 06 12 00 0a b1 22 84 f7 e6 0f b4 5c .........................".....\ 21.1.0
90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 21.1.32
29.1 OUT f0 fe 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1c 00 00 07 12 00 19 80 18 00 01 00 00 12 10 ................................ 22.1.0
00 cf 87 86 2f eb 06 65 9e d8 8a 10 61 09 e7 42 f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ..../..e....a..B................ 22.1.32
29.1 IN d9 aa aa 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 07 12 00 02 90 00 00 00 00 00 00 00 ................................ 23.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 23.1.32
29.1 OUT e0 fe 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0c 00 00 08 12 00 09 80 46 00 00 00 00 02 10 .........................F...... 24.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 24.1.32
29.1 IN eb aa aa 17 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 12 00 14 44 65 66 61 75 6c 74 43 ........................DefaultC 25.1.0
6f 6e 74 61 69 6e 65 72 00 00 90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ontainer........................ 25.1.32
29.1 OUT f2 fe 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1e 00 00 09 12 00 1b 80 42 00 00 00 00 12 10 .........................B...... 26.1.0
00 44 65 66 61 75 6c 74 43 6f 6e 74 61 69 6e 65 72 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 .DefaultContainer............... 26.1.32
29.1 IN db aa aa 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09 12 00 04 30 01 90 00 00 00 00 00 ........................0....... 27.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 27.1.32
29.1 OUT db fe 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 07 00 00 0a 12 00 04 c0 52 00 00 00 00 00 00 .........................R...... 28.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 28.1.32
29.1 IN d9 aa aa 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0a 12 00 02 90 00 00 00 00 00 00 00 ................................ 29.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 29.1.32
29.1 OUT e4 fe 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 0b 12 00 0d 80 4e 01 00 00 00 04 10 .........................N...... 30.1.0
00 30 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 .0.............................. 30.1.32
29.1 IN bf aa aa f5 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0b 12 01 f2 00 00 03 82 2d 2d 2d 2d ............................---- 31.1.0
2d 42 45 47 49 4e 20 43 45 52 54 49 46 49 43 41 54 45 2d 2d 2d 2d 2d 0d 0a 4d 49 49 43 58 7a 43 -BEGIN CERTIFICATE-----..MIICXzC 31.1.32
29.1 IN 3f 43 41 67 57 67 41 77 49 42 41 67 49 49 52 64 4f 6f 49 6f 58 58 64 4d 41 77 43 67 59 49 4b 6f ?CAgWgAwIBAgIIRdOoIoXXdMAwCgYIKo 32.1.0
45 63 7a 31 55 42 67 33 55 77 4e 6a 45 4c 4d 41 6b 47 41 31 55 45 42 68 4d 43 0d 0a 51 30 34 78 Ecz1UBg3UwNjELMAkGA1UEBhMC..Q04x 32.1.32
29.1 IN 3f 45 7a 41 52 42 67 4e 56 42 41 6f 54 43 6c 4e 6f 59 57 35 6e 54 57 6c 43 5a 57 6b 78 45 6a 41 ?EzARBgNVBAoTClNoYW5nTWlCZWkxEjA 33.1.0
51 42 67 4e 56 42 41 4d 54 43 56 4e 6f 59 57 35 6e 54 57 6c 44 51 54 41 65 46 77 30 79 0d 0a 4e QBgNVBAMTCVNoYW5nTWlDQTAeFw0y..N 33.1.32
29.1 IN 3f 54 41 32 4d 44 6b 77 4d 6a 55 77 4e 44 6c 61 46 77 30 30 4e 54 45 77 4d 54 41 78 4d 6a 41 78 ?TA2MDkwMjUwNDlaFw00NTEwMTAxMjAx 34.1.0
4d 44 46 61 4d 46 55 78 45 7a 41 52 42 67 4e 56 42 41 6f 54 43 6c 4e 6f 59 57 35 6e 54 57 6c 43 MDFaMFUxEzARBgNVBAoTClNoYW5nTWlC 34.1.32
29.1 IN 3f 0d 0a 5a 57 6b 78 46 7a 41 56 42 67 4e 56 42 41 73 54 44 6c 4e 6f 59 57 35 6e 54 57 6c 43 5a ?..ZWkxFzAVBgNVBAsTDlNoYW5nTWlCZ 35.1.0
57 6b 79 4d 44 49 31 4d 52 67 77 46 67 59 44 56 51 51 44 45 77 39 7a 61 47 46 75 5a 32 31 70 59 WkyMDI1MRgwFgYDVQQDEw9zaGFuZ21pY 35.1.32
29.1 IN 3f 6d 56 70 0d 0a 59 57 52 74 61 57 34 78 43 7a 41 4a 42 67 4e 56 42 41 59 54 41 6b 4e 4f 4d 46 ?mVp..YWRtaW4xCzAJBgNVBAYTAkNOMF 36.1.0
6b 77 45 77 59 48 4b 6f 5a 49 7a 6a 30 43 41 51 59 49 4b 6f 45 63 7a 31 55 42 67 69 30 44 51 67 kwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQg 36.1.32
29.1 IN 3f 41 45 37 2b 74 35 0d 0a 34 51 6f 78 4c 50 48 49 68 78 6b 64 41 54 65 6d 62 45 66 69 52 62 2f ?AE7+t5..4QoxLPHIhxkdATembEfiRb/ 37.1.0
2f 4b 38 48 42 6e 39 4c 34 72 4a 71 56 4d 62 38 64 47 4e 32 51 39 51 38 41 52 75 55 53 75 56 37 /K8HBn9L4rJqVMb8dGN2Q9Q8ARuUSuV7 37.1.32
29.1 IN 3f 71 33 6f 5a 50 78 4a 34 77 0d 0a 73 6b 73 39 56 45 76 55 2f 41 68 6b 39 30 43 79 36 61 4f 42 ?q3oZPxJ4w..sks9VEvU/Ahk90Cy6aOB 38.1.0
33 54 43 42 32 6a 41 4f 42 67 4e 56 48 51 38 42 41 66 38 45 42 41 4d 43 41 34 67 77 48 51 59 44 3TCB2jAOBgNVHQ8BAf8EBAMCA4gwHQYD 38.1.32
29.1 IN 51 56 52 30 6c 42 42 59 77 46 41 59 49 0d 0a 4b 6a 9e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 QVR0lBBYwFAYI..Kj............... 39.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 39.1.32
29.1 OUT e4 fe 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 0c 12 00 0d 80 4e 01 00 00 00 04 10 .........................N...... 40.1.0
00 30 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 .0.............................. 40.1.32
29.1 IN bf aa aa 9b 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0c 12 01 98 77 59 42 42 51 55 48 41 ........................wYBBQUHA 41.1.0
77 49 47 43 43 73 47 41 51 55 46 42 77 4d 42 4d 41 38 47 41 31 55 64 44 67 51 49 42 41 59 42 41 wIGCCsGAQUFBwMBMA8GA1UdDgQIBAYBA 41.1.32
29.1 IN 3f 67 4d 45 42 51 59 77 44 77 59 44 56 52 30 6a 42 41 67 77 42 6f 41 45 0d 0a 41 51 49 44 42 44 ?gMEBQYwDwYDVR0jBAgwBoAE..AQIDBD 42.1.0
41 75 42 67 4e 56 48 52 45 45 4a 7a 41 6c 67 51 74 6e 61 58 52 41 5a 32 6c 30 4c 6d 4e 76 62 59 AuBgNVHREEJzAlgQtnaXRAZ2l0LmNvbY 42.1.32
29.1 IN 3f 63 45 66 77 41 41 41 59 63 51 49 41 46 49 59 41 41 41 49 41 45 41 41 41 41 41 0d 0a 41 41 41 ?cEfwAAAYcQIAFIYAAAIAEAAAAA..AAA 43.1.0
41 61 44 42 58 42 67 4e 56 48 52 38 45 55 44 42 4f 4d 43 57 67 49 36 41 68 68 68 39 6f 64 48 52 AaDBXBgNVHR8EUDBOMCWgI6Ahhh9odHR 43.1.32
29.1 IN 3f 77 4f 69 38 76 59 33 4a 73 4d 53 35 6c 65 47 46 74 63 47 78 6c 4c 6d 4e 76 62 53 39 6a 0d 0a ?wOi8vY3JsMS5leGFtcGxlLmNvbS9j.. 44.1.0
59 54 45 75 59 33 4a 73 4d 43 57 67 49 36 41 68 68 68 39 6f 64 48 52 77 4f 69 38 76 59 33 4a 73 YTEuY3JsMCWgI6Ahhh9odHRwOi8vY3Js 44.1.32
29.1 IN 3f 4d 69 35 6c 65 47 46 74 63 47 78 6c 4c 6d 4e 76 62 53 39 6a 59 54 45 75 59 33 4a 73 4d 41 6f ?Mi5leGFtcGxlLmNvbS9jYTEuY3JsMAo 45.1.0
47 0d 0a 43 43 71 42 48 4d 39 56 41 59 4e 31 41 30 67 41 4d 45 55 43 49 41 64 2b 6d 6d 50 75 4d G..CCqBHM9VAYN1A0gAMEUCIAd+mmPuM 45.1.32
29.1 IN 3f 2f 43 79 2b 2f 44 31 43 73 38 62 57 47 56 31 65 39 6d 76 72 63 4d 36 52 5a 39 4e 48 78 57 47 ?/Cy+/D1Cs8bWGV1e9mvrcM6RZ9NHxWG 46.1.0
48 50 6c 74 0d 0a 41 69 45 41 6a 76 34 31 34 77 45 6d 6c 5a 64 33 50 55 37 41 6b 59 61 4f 35 44 HPlt..AiEAjv414wEmlZd3PU7AkYaO5D 46.1.32
29.1 IN 75 7a 36 47 62 56 6f 58 78 77 6a 30 52 4f 52 39 4f 48 2b 44 76 77 3d 0d 0a 2d 2d 2d 2d 2d 45 4e uz6GbVoXxwj0ROR9OH+Dvw=..-----EN 47.1.0
44 20 43 45 52 54 49 46 49 43 41 54 45 2d 2d 2d 2d 2d 0d 0a 90 00 00 00 00 00 00 00 00 00 00 00 D CERTIFICATE-----.............. 47.1.32
29.1 OUT e4 fe 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 0d 12 00 0d 80 88 01 00 00 00 04 10 ................................ 48.1.0
00 30 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 .0.............................. 48.1.32
29.1 IN bf aa aa 49 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0d 12 00 46 00 00 01 00 ef eb 79 e1 ...I...................F......y. 49.1.0
0a 31 2c f1 c8 87 19 1d 01 37 a6 6c 47 e2 45 bf ff 2b c1 c1 9f d2 f8 ac 9a 95 31 bf 1d 18 dd 90 .1,......7.lG.E..+........1..... 49.1.32
29.1 IN 5e f5 0f 00 46 e5 12 b9 5e ea de 86 4f c4 9e 30 b2 4b 3d 54 4b d4 fc 08 64 f7 40 b2 e9 90 00 00 ^...F...^...O..0.K=TK...d.@..... 50.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 50.1.32
29.1 OUT de fe 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0a 00 00 0e 12 00 07 80 04 00 00 00 00 00 00 ................................ 51.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 51.1.32
29.1 IN bf aa aa 25 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0e 12 01 22 01 00 01 00 4c 6f 6e 67 ...%..................."....Long 52.1.0
6d 61 69 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 mai............................. 52.1.32
29.1 IN 3f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 4c 6f 6e ?............................Lon 53.1.0
67 6d 61 69 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 gmai............................ 53.1.32
29.1 IN 3f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 47 4d ?.............................GM 54.1.0
33 30 30 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 37 39 3000..........................79 54.1.32
29.1 IN 3f 37 33 33 30 41 31 35 30 46 33 36 41 45 45 38 39 33 39 43 42 36 41 36 39 36 39 41 30 44 00 05 ?7330A150F36AEE8939CB6A6969A0D.. 55.1.0
00 02 17 07 07 00 00 00 07 03 00 07 00 00 00 01 04 00 00 00 02 00 00 00 01 ee 80 04 20 00 01 00 ............................ ... 55.1.32
29.1 IN 7d 02 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 56.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90 00 00 00 ................................ 56.1.32
29.1 OUT bf fe 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 62 00 00 0f 12 00 5f 80 b4 00 01 00 00 58 00 .................b....._......X. 57.1.0
00 01 00 ef eb 79 e1 0a 31 2c f1 c8 87 19 1d 01 37 a6 6c 47 e2 45 bf ff 2b c1 c1 9f d2 f8 ac 9a .....y..1,......7.lG.E..+....... 57.1.32
29.1 OUT 77 95 31 bf 1d 18 dd 90 f5 0f 00 46 e5 12 b9 5e ea de 86 4f c4 9e 30 b2 4b 3d 54 4b d4 fc 08 64 w.1........F...^...O..0.K=TK...d 58.1.0
f7 40 b2 e9 00 00 00 10 31 32 33 34 35 36 37 38 31 32 33 34 35 36 37 38 00 00 00 00 00 00 00 00 .@......1234567812345678........ 58.1.32
29.1 IN d9 aa aa 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0f 12 00 02 90 00 00 00 00 00 00 00 ................................ 59.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 59.1.32
29.1 OUT bf fe 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 4c 00 00 10 12 00 49 80 b6 00 00 00 00 40 61 .................L.....I......@a 60.1.0
73 64 6b 66 68 6a 32 33 73 6c 61 6a 64 66 39 32 33 61 66 64 73 6c 6a 31 33 6b 7a 48 66 31 72 6f sdkfhj23slajdf923afdslj13kzHf1ro 60.1.32
29.1 OUT 61 69 75 6c 73 61 64 6a 66 61 6c 73 31 32 73 6c 64 6a 30 75 32 33 72 32 33 61 6e 73 76 30 6a 32 aiulsadjfals12sldj0u23r23ansv0j2 61.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 61.1.32
29.1 IN f9 aa aa 25 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 12 00 22 b0 e5 68 94 2a 01 d2 2d ...%..................."..h.*..- 62.1.0
2c 7e e4 4c 85 e2 2e 95 f1 78 cd 1f 86 a9 78 ca bc 96 1b a0 24 d8 60 71 90 00 00 00 00 00 00 00 ,..L.....x....x.....$.`q........ 62.1.32
29.1 OUT bf fe 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 2e 00 00 11 12 00 2b 80 74 02 00 00 00 24 10 .......................+.t....$. 63.1.0
00 30 01 b0 e5 68 94 2a 01 d2 2d 2c 7e e4 4c 85 e2 2e 95 f1 78 cd 1f 86 a9 78 ca bc 96 1b a0 24 .0...h.*..-,..L.....x....x.....$ 63.1.32
29.1 OUT 43 d8 60 71 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C.`q............................ 64.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 64.1.32
29.1 IN bf aa aa 49 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 11 12 00 46 00 00 01 00 8e f2 a9 51 ...I...................F.......Q 65.1.0
b5 19 4e 0b 5c 98 a6 16 0b d7 9e 02 4b b9 58 58 04 ae dc 97 3a 9f ce 6d cd e2 17 03 9e 70 c5 42 ..N.\.......K.XX....:..m.....p.B 65.1.32
29.1 IN 5e 83 63 8b 6f ba 3c 00 3b d9 17 24 47 ce 4f aa 4c 0e 8b b7 89 57 54 ac 7b 0e 55 d0 44 90 00 00 ^.c.o.<.;..$G.O.L....WT...U.D... 66.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 66.1.32
29.1 OUT e2 fe 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0e 00 00 12 12 00 0b 80 44 00 00 00 00 04 10 .........................D...... 67.1.0
00 30 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 .0.............................. 67.1.32
29.1 IN d9 aa aa 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 12 12 00 02 90 00 00 00 00 00 00 00 ................................ 68.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 68.1.32

其实题目目标很简单,我们只要把证书、签名值和 hash 摘要提取出来就好了。

根据 GB-T 0017-2023,提证书的话,我们先找 OUT 中 80 4e 字段,然后把下面的值都提取出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
29.1  IN     bf aa aa f5  01 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  0b 12 01 f2  00 00 03 82  2d 2d 2d 2d  ............................----        31.1.0        
2d 42 45 47 49 4e 20 43 45 52 54 49 46 49 43 41 54 45 2d 2d 2d 2d 2d 0d 0a 4d 49 49 43 58 7a 43 -BEGIN CERTIFICATE-----..MIICXzC 31.1.32
29.1 IN 3f 43 41 67 57 67 41 77 49 42 41 67 49 49 52 64 4f 6f 49 6f 58 58 64 4d 41 77 43 67 59 49 4b 6f ?CAgWgAwIBAgIIRdOoIoXXdMAwCgYIKo 32.1.0
45 63 7a 31 55 42 67 33 55 77 4e 6a 45 4c 4d 41 6b 47 41 31 55 45 42 68 4d 43 0d 0a 51 30 34 78 Ecz1UBg3UwNjELMAkGA1UEBhMC..Q04x 32.1.32
29.1 IN 3f 45 7a 41 52 42 67 4e 56 42 41 6f 54 43 6c 4e 6f 59 57 35 6e 54 57 6c 43 5a 57 6b 78 45 6a 41 ?EzARBgNVBAoTClNoYW5nTWlCZWkxEjA 33.1.0
51 42 67 4e 56 42 41 4d 54 43 56 4e 6f 59 57 35 6e 54 57 6c 44 51 54 41 65 46 77 30 79 0d 0a 4e QBgNVBAMTCVNoYW5nTWlDQTAeFw0y..N 33.1.32
29.1 IN 3f 54 41 32 4d 44 6b 77 4d 6a 55 77 4e 44 6c 61 46 77 30 30 4e 54 45 77 4d 54 41 78 4d 6a 41 78 ?TA2MDkwMjUwNDlaFw00NTEwMTAxMjAx 34.1.0
4d 44 46 61 4d 46 55 78 45 7a 41 52 42 67 4e 56 42 41 6f 54 43 6c 4e 6f 59 57 35 6e 54 57 6c 43 MDFaMFUxEzARBgNVBAoTClNoYW5nTWlC 34.1.32
29.1 IN 3f 0d 0a 5a 57 6b 78 46 7a 41 56 42 67 4e 56 42 41 73 54 44 6c 4e 6f 59 57 35 6e 54 57 6c 43 5a ?..ZWkxFzAVBgNVBAsTDlNoYW5nTWlCZ 35.1.0
57 6b 79 4d 44 49 31 4d 52 67 77 46 67 59 44 56 51 51 44 45 77 39 7a 61 47 46 75 5a 32 31 70 59 WkyMDI1MRgwFgYDVQQDEw9zaGFuZ21pY 35.1.32
29.1 IN 3f 6d 56 70 0d 0a 59 57 52 74 61 57 34 78 43 7a 41 4a 42 67 4e 56 42 41 59 54 41 6b 4e 4f 4d 46 ?mVp..YWRtaW4xCzAJBgNVBAYTAkNOMF 36.1.0
6b 77 45 77 59 48 4b 6f 5a 49 7a 6a 30 43 41 51 59 49 4b 6f 45 63 7a 31 55 42 67 69 30 44 51 67 kwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQg 36.1.32
29.1 IN 3f 41 45 37 2b 74 35 0d 0a 34 51 6f 78 4c 50 48 49 68 78 6b 64 41 54 65 6d 62 45 66 69 52 62 2f ?AE7+t5..4QoxLPHIhxkdATembEfiRb/ 37.1.0
2f 4b 38 48 42 6e 39 4c 34 72 4a 71 56 4d 62 38 64 47 4e 32 51 39 51 38 41 52 75 55 53 75 56 37 /K8HBn9L4rJqVMb8dGN2Q9Q8ARuUSuV7 37.1.32
29.1 IN 3f 71 33 6f 5a 50 78 4a 34 77 0d 0a 73 6b 73 39 56 45 76 55 2f 41 68 6b 39 30 43 79 36 61 4f 42 ?q3oZPxJ4w..sks9VEvU/Ahk90Cy6aOB 38.1.0
33 54 43 42 32 6a 41 4f 42 67 4e 56 48 51 38 42 41 66 38 45 42 41 4d 43 41 34 67 77 48 51 59 44 3TCB2jAOBgNVHQ8BAf8EBAMCA4gwHQYD 38.1.32
29.1 IN 51 56 52 30 6c 42 42 59 77 46 41 59 49 0d 0a 4b 6a 9e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 QVR0lBBYwFAYI..Kj............... 39.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 39.1.32
29.1 OUT e4 fe 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 0c 12 00 0d 80 4e 01 00 00 00 04 10 .........................N...... 40.1.0
00 30 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 .0.............................. 40.1.32
29.1 IN bf aa aa 9b 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0c 12 01 98 77 59 42 42 51 55 48 41 ........................wYBBQUHA 41.1.0
77 49 47 43 43 73 47 41 51 55 46 42 77 4d 42 4d 41 38 47 41 31 55 64 44 67 51 49 42 41 59 42 41 wIGCCsGAQUFBwMBMA8GA1UdDgQIBAYBA 41.1.32
29.1 IN 3f 67 4d 45 42 51 59 77 44 77 59 44 56 52 30 6a 42 41 67 77 42 6f 41 45 0d 0a 41 51 49 44 42 44 ?gMEBQYwDwYDVR0jBAgwBoAE..AQIDBD 42.1.0
41 75 42 67 4e 56 48 52 45 45 4a 7a 41 6c 67 51 74 6e 61 58 52 41 5a 32 6c 30 4c 6d 4e 76 62 59 AuBgNVHREEJzAlgQtnaXRAZ2l0LmNvbY 42.1.32
29.1 IN 3f 63 45 66 77 41 41 41 59 63 51 49 41 46 49 59 41 41 41 49 41 45 41 41 41 41 41 0d 0a 41 41 41 ?cEfwAAAYcQIAFIYAAAIAEAAAAA..AAA 43.1.0
41 61 44 42 58 42 67 4e 56 48 52 38 45 55 44 42 4f 4d 43 57 67 49 36 41 68 68 68 39 6f 64 48 52 AaDBXBgNVHR8EUDBOMCWgI6Ahhh9odHR 43.1.32
29.1 IN 3f 77 4f 69 38 76 59 33 4a 73 4d 53 35 6c 65 47 46 74 63 47 78 6c 4c 6d 4e 76 62 53 39 6a 0d 0a ?wOi8vY3JsMS5leGFtcGxlLmNvbS9j.. 44.1.0
59 54 45 75 59 33 4a 73 4d 43 57 67 49 36 41 68 68 68 39 6f 64 48 52 77 4f 69 38 76 59 33 4a 73 YTEuY3JsMCWgI6Ahhh9odHRwOi8vY3Js 44.1.32
29.1 IN 3f 4d 69 35 6c 65 47 46 74 63 47 78 6c 4c 6d 4e 76 62 53 39 6a 59 54 45 75 59 33 4a 73 4d 41 6f ?Mi5leGFtcGxlLmNvbS9jYTEuY3JsMAo 45.1.0
47 0d 0a 43 43 71 42 48 4d 39 56 41 59 4e 31 41 30 67 41 4d 45 55 43 49 41 64 2b 6d 6d 50 75 4d G..CCqBHM9VAYN1A0gAMEUCIAd+mmPuM 45.1.32
29.1 IN 3f 2f 43 79 2b 2f 44 31 43 73 38 62 57 47 56 31 65 39 6d 76 72 63 4d 36 52 5a 39 4e 48 78 57 47 ?/Cy+/D1Cs8bWGV1e9mvrcM6RZ9NHxWG 46.1.0
48 50 6c 74 0d 0a 41 69 45 41 6a 76 34 31 34 77 45 6d 6c 5a 64 33 50 55 37 41 6b 59 61 4f 35 44 HPlt..AiEAjv414wEmlZd3PU7AkYaO5D 46.1.32
29.1 IN 75 7a 36 47 62 56 6f 58 78 77 6a 30 52 4f 52 39 4f 48 2b 44 76 77 3d 0d 0a 2d 2d 2d 2d 2d 45 4e uz6GbVoXxwj0ROR9OH+Dvw=..-----EN 47.1.0
44 20 43 45 52 54 49 46 49 43 41 54 45 2d 2d 2d 2d 2d 0d 0a 90 00 00 00 00 00 00 00 00 00 00 00 D CERTIFICATE-----.............. 47.1.32

这里注意对于连续的 IN,从第二个开始第一个字符是多余的要删去。然后我们从第一行的倒数第 8 个字节符开始看, 00 00 03 82 表示证书长度,后面开始才是证书。然后在第一个连续的 IN 的末尾有一个 6a 9e 表示证书还没有传完,那我们就要往下接着看,下面那个 OUT 不用管,然后继续从倒数第 8 个字节开始才是证书,最后末尾是 90 00 表示证书已经传完了。

然后找 hash 值,找 80 b6 字段,可以拿到

1
2
3
4
5
29.1  OUT    bf fe 01 00  00 00 00 00  00 00 00 00  00 00 00 00  00 4c 00 00  10 12 00 49  80 b6 00 00  00 00 40 61  .................L.....I......@a        60.1.0        
73 64 6b 66 68 6a 32 33 73 6c 61 6a 64 66 39 32 33 61 66 64 73 6c 6a 31 33 6b 7a 48 66 31 72 6f sdkfhj23slajdf923afdslj13kzHf1ro 60.1.32
29.1 OUT 61 69 75 6c 73 61 64 6a 66 61 6c 73 31 32 73 6c 64 6a 30 75 32 33 72 32 33 61 6e 73 76 30 6a 32 aiulsadjfals12sldj0u23r23ansv0j2 61.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 61.1.32

然后后面就是 hash 值了,直接提取就好了,注意第二个 OUT的第一个字节要删去

最后就是取签名值,还是 80 74 字段,不过在后面的 IN 里

1
2
3
4
29.1  IN     bf aa aa 49  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  11 12 00 46  00 00 01 00  8e f2 a9 51  ...I...................F.......Q        65.1.0        
b5 19 4e 0b 5c 98 a6 16 0b d7 9e 02 4b b9 58 58 04 ae dc 97 3a 9f ce 6d cd e2 17 03 9e 70 c5 42 ..N.\.......K.XX....:..m.....p.B 65.1.32
29.1 IN 5e 83 63 8b 6f ba 3c 00 3b d9 17 24 47 ce 4f aa 4c 0e 8b b7 89 57 54 ac 7b 0e 55 d0 44 90 00 00 ^.c.o.<.;..$G.O.L....WT...U.D... 66.1.0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................................ 66.1.32

同样的,从倒数第 8 个字节开始,00 00 01 00 表模数的 bit 大小,后面就是签名值了,注意连续的 IN 里第一个字符要删去

最后就能登陆了

2-TSP服务器鉴别APP控制端

题目:

auth_client.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package client

/*
* 日期:2025/5/21
* 用途:TSP 服务器鉴别 APP 控制端身份
* 作者:X
*/

import (
"crypto/elliptic"
"crypto/rand"
"encoding/base64"
"errors"
"github.com/tjfoc/gmsm/sm2"
"io"
"math/big"
)

var errZeroParam = errors.New("zero parameter")
var one = new(big.Int).SetInt64(1)

type Point struct {
X, Y *big.Int
}

func randFieldElement(c elliptic.Curve, random io.Reader) (k *big.Int, err error) {
if random == nil {
random = rand.Reader //If there is no external trusted random source,please use rand.Reader to instead of it.
}
params := c.Params()
b := make([]byte, params.BitSize/8+8)
_, err = io.ReadFull(random, b)
if err != nil {
return
}
k = new(big.Int).SetBytes(b)
n := new(big.Int).Sub(params.N, one)
k.Mod(k, n)
k.Add(k, one)
return
}

type AuthClient struct {
c elliptic.Curve
N *big.Int
commonPublicKey *sm2.PublicKey
D1 *big.Int
random io.Reader

k1 *big.Int
}

// passwd 为 APP 控制端的登录口令
func ComputeD1(passwd string) (*big.Int, error) {
d1Bytes, err := base64.StdEncoding.DecodeString(passwd)
if err != nil {
return nil, err
}
return new(big.Int).SetBytes(d1Bytes), nil
}

func NewAuthClient(commonPublicKey *sm2.PublicKey, D1 *big.Int, random io.Reader) (*AuthClient, error) {
c := sm2.P256Sm2()
N := c.Params().N
if N.Sign() == 0 {
return nil, errZeroParam
}
return &AuthClient{c: c, N: N, commonPublicKey: commonPublicKey, D1: D1, random: random}, nil
}

func (signer *AuthClient) GenerateQ1E(msg, uid []byte) (Q1 *Point, e *big.Int, err error) {
digest, err := signer.commonPublicKey.Sm3Digest(msg, uid)
if err != nil {
return nil, nil, err
}
e = new(big.Int).SetBytes(digest)

var k1 *big.Int
k1, err = randFieldElement(signer.c, signer.random)
if err != nil {
return nil, nil, err
}
signer.k1 = k1
x1, y1 := signer.c.ScalarBaseMult(k1.Bytes())
Q1 = &Point{X: x1, Y: y1}

return Q1, e, nil
}

func (signer *AuthClient) GenerateS(r, s2, s3 *big.Int) (s *big.Int, err error) {
t1 := new(big.Int).Mul(signer.D1, signer.k1)
t1.Mul(t1, s2)
t2 := new(big.Int).Mul(signer.D1, s3)
t3 := t1.Add(t1, t2)
s = t3.Sub(t3, r)
s.Mod(s, signer.N)

nMinusR := new(big.Int).Sub(signer.N, r)
if s.Sign() != 0 && s.Cmp(nMinusR) != 0 {
return s, nil
} else {
return nil, errors.New("invalid s")
}
}

auth_server.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package server

/*
* 日期:2025/5/21
* 用途:TSP 服务器鉴别 APP 控制端身份
* 作者:X
*/

import (
"crypto/elliptic"
"crypto/rand"
"errors"
"github.com/tjfoc/gmsm/sm2"
"io"
"math/big"
)

var errZeroParam = errors.New("zero parameter")
var one = new(big.Int).SetInt64(1)

type Point struct {
X, Y *big.Int
}

func randFieldElement(c elliptic.Curve, random io.Reader) (k *big.Int, err error) {
if random == nil {
random = rand.Reader //If there is no external trusted random source,please use rand.Reader to instead of it.
}
params := c.Params()
b := make([]byte, params.BitSize/8+8)
_, err = io.ReadFull(random, b)
if err != nil {
return
}
k = new(big.Int).SetBytes(b)
n := new(big.Int).Sub(params.N, one)
k.Mod(k, n)
k.Add(k, one)
return
}

type AuthServer struct {
c elliptic.Curve
N *big.Int
commonPublicKey *sm2.PublicKey
D2 *big.Int
random io.Reader
}

func NewAuthServer(commonPublicKey *sm2.PublicKey, D2 *big.Int, random io.Reader) (*AuthServer, error) {
c := sm2.P256Sm2()
N := c.Params().N
if N.Sign() == 0 {
return nil, errZeroParam
}
return &AuthServer{c: c, N: N, commonPublicKey: commonPublicKey, D2: D2, random: random}, nil
}

func (signer *AuthServer) GenerateRS2S3(Q1 *Point, e *big.Int) (r, s2, s3 *big.Int, err error) {
x1, y1 := Q1.X, Q1.Y

for {
var k2 *big.Int
k2, err = randFieldElement(signer.c, signer.random)
if err != nil {
return nil, nil, nil, err
}
x2, y2 := signer.c.ScalarBaseMult(k2.Bytes()) // k2 * G

var k3 *big.Int
k3, err = randFieldElement(signer.c, signer.random)
if err != nil {
return nil, nil, nil, err
}

tempX, tempY := signer.c.ScalarMult(x1, y1, k3.Bytes())
x3, _ := signer.c.Add(tempX, tempY, x2, y2)

r = new(big.Int).Add(x3, e)
r.Mod(r, signer.N)

if r.Sign() != 0 {
s2 = new(big.Int).Mul(signer.D2, k3)
s2.Mod(s2, signer.N)

s3 = new(big.Int).Add(r, k2)
s3.Mul(s3, signer.D2)
s3.Mod(s3, signer.N)
break
}
}
return r, s2, s3, nil
}

func (signer *AuthServer) Verify(msg []byte, uid []byte, r *big.Int, s *big.Int) bool {
return sm2.Sm2Verify(signer.commonPublicKey, msg, uid, r, s)
}

是一个基于 SM2 的协同签名算法,然后其实就是去年熵密杯最后的协同签名算法流程,完全一样的,这里就不再多做叙述了。

当时没有截图,只能简单讲述一下,就是 APP 客户端会给 TSP 服务器发送 Q1Q_1ee。然后 TSP 服务器会生成自己的 r,s2,s3r,s_2,s_3,最后你要点击一个按钮,这样子 TSP 服务器会把这些值发送回 APP 客户端然后计算 ss。我们的目标是计算 d1d_1

这时候就有个好玩的点了,就是我们要点一个按钮才会传参,那我们可以在这个时候进行抓包重新伪造 r,s2,s3r,s_2,s_3,我们注意 ss 的生成方式

s=d1k1s2+d1s3rmodns=d_1k_1s_2+d_1s_3-r\mod n

如果我们可以让 s2s_2 为 0,那么这个式子只有 d1d_1 是未知的了。所以我们直接打一个重放攻击,修改 s2s_2 为 0 即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from Crypto.Util.number import *
from sage.all import *


import base64
n = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123
d = PolynomialRing(Zmod(n), name='d').gen()

s = 0x8539bcb663238748bfc1fa0f417359d64b46ef6e46abbe7b2cef3f8a4cddc73f
r = 0x93b5851e2a534f38a3edf7bada4b50f3c2326273117f956677e5f9ca0219cced
s2 = 0
s3 = 0x7a3e83db456607b842aa4a37511a155717f9e3fd82120920d8917edaa1f86a11
f1 = d * s3 - r - s

print(base64.b64encode(long_to_bytes(int(f1.roots()[0][0]))))

3-目标地点经纬度加密

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "sdf_cryptoapi.h"

void *devHandle = NULL;
void *sessionHandle = NULL;
void *keyHandle = NULL;

const unsigned char encrypt_key_hex[] = "FE61D7ED4A6376AAF13E7D78CB42E645";

int open_session() {
int ret = 0;

//打开设备
ret = SDF_OpenDeviceWithPath("./sdt_hsmcrypt.conf", &devHandle);
if (ret) {
printf("SDF_OpenDeviceWithPath error, ret=%08x\n", ret);
return ret;
}

//打开会话
ret = SDF_OpenSession(devHandle, &sessionHandle);
if (ret) {
printf("SDF_OpenSession error, ret=%08x\n", ret);
SDF_CloseDevice(devHandle);
return ret;
}
return ret;
}

int hex_string_to_char_array(const char *hex, unsigned char *out) {
size_t hex_len = strlen(hex);
if (hex_len % 2 != 0) return -1; // 奇数长度报错

for (size_t i = 0; i < hex_len; i += 2) {
unsigned int byte;
if (sscanf(hex + i, "%2X", &byte) != 1) return -1; // 解析失败
out[i / 2] = (unsigned char) byte;
}
return hex_len / 2; // 返回转换后的字节数
}

int import_key() {
int ret = 0;

unsigned char encrypt_key[128] = {'\0'};
const unsigned int key_len = 16;
const int inner_key_index = 1;
ret = hex_string_to_char_array(encrypt_key_hex, encrypt_key);
if (ret < 0) {
printf("hex_string_to_char_array error, ret=%08x\n", ret);
return ret;
}

ret = SDF_ImportKeyWithKEK(sessionHandle, SGD_SM4, inner_key_index, encrypt_key, key_len, &keyHandle);
if (ret) {
printf("SDF_ImportKeyWithKEK error, ret=%08x\n", ret);
return ret;
}
return 0;
}

int init() {
int ret = 0;
ret = open_session();
if (ret) {
printf("open_session error, ret=%08x\n", ret);
return ret;
}
ret = import_key();
if (ret) {
printf("import_key error, ret=%08x\n", ret);
return ret;
}
return 0;
}

int close_session() {
//关闭会话
int ret = 0;
ret = SDF_CloseSession(sessionHandle);
if (ret) {
printf("SDF_CloseSession error, ret=%08x\n", ret);
SDF_CloseDevice(devHandle);
return ret;
}

//关闭设备
ret = SDF_CloseDevice(devHandle);
if (ret) {
printf("SDF_CloseDevice error, ret=%08x\n", ret);
return ret;
}
return ret;
}

int destroy() {
int ret = 0;

//销毁密钥句柄
ret = SDF_DestroyKey(sessionHandle, keyHandle);
if (ret) {
printf("[ts_symm] SDF_DestroyKey error, ret=%08x\n", ret);
return ret;
}

ret = close_session();
if (ret) {
printf("[ts_symm] close_session error, ret=%08x\n", ret);
return ret;
}
return 0;
}

int encrypt_with_sm4_cfb(unsigned char *iv, unsigned char *plaintext, unsigned int plaintext_len,
unsigned char *ciphertext,
unsigned int *ciphertext_len) {
// TODO 此处代码已省略
return 0;
}

unsigned char *transform(const unsigned char *input, size_t in_len) {
if (in_len < 16) {
return NULL;
}

unsigned char *output = malloc(in_len + 1);
if (output == NULL) {
return NULL;
}
memcpy(output, input, 8);
memcpy(output + 8, input + in_len - 8, 8);
memcpy(output + 8 * 2, input + 8, in_len - 8 * 2);
output[in_len] = '\0';
return output;
}

unsigned char *pkcs7_padding(const unsigned char *input, size_t in_len, size_t block_size, size_t *out_len) {
// TODO 此处代码已省略
return NULL;
}

const char hex_table[] = "0123456789ABCDEF";

void char_array_to_hex_string(const unsigned char *src, size_t len, char *dst) {
for (size_t i = 0; i < len; i++) {
dst[2 * i] = hex_table[(src[i] >> 4) & 0x0F];
dst[2 * i + 1] = hex_table[src[i] & 0x0F];
}
dst[2 * len] = '\0';
}

// 加密地理位置经纬度
int encrypt_geo_location(unsigned char *iv, const unsigned char *location, unsigned int location_len,
char *ciphertext_hex,
unsigned int *ciphertext_hex_len) {
int ret = 0;
unsigned char buffer[1024] = {0};
// 1. 转换格式
unsigned char *transformed_location = transform(location, location_len);

// 2. 填充
size_t padded_plaintext_len = 0;
unsigned char *padded_plaintext = pkcs7_padding(transformed_location, location_len, 16, &padded_plaintext_len);
if (padded_plaintext == NULL) {
ret = -1;
printf("pkcs7_padding failed!");
free(transformed_location);
return ret;
}

// 3. 加密
unsigned int ciphertext_len = 0;
ret = encrypt_with_sm4_cfb(iv, padded_plaintext, padded_plaintext_len, buffer, &ciphertext_len);
if (ret) {
printf("encrypt error, ret=%08x\n", ret);
free(transformed_location);
free(padded_plaintext);
free(iv);
return ret;
}
char_array_to_hex_string(buffer, ciphertext_len, ciphertext_hex);
*ciphertext_hex_len = 2 * ciphertext_len;

// 4. 清理
free(transformed_location);
free(padded_plaintext);
return ret;
}

int main(int argc, char *argv[]) {
int ret = 0;
const unsigned char location[] = "xxxxxxxxxxxxxxxxxxxx";
const unsigned int location_len = sizeof(location) - 1;

ret = init();
if (ret) {
printf("init error, ret=%08x\n", ret);
return ret;
}

char ciphertext_hex[1024] = {0};
unsigned int ciphertext_hex_len = 0;
unsigned char iv[] = {'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'};

ret = encrypt_geo_location(iv, location, location_len, ciphertext_hex, &ciphertext_hex_len);
if (ret) {
printf("encrypt error, ret=%08x\n", ret);
return ret;
}
printf("encrypt ok\n");
printf("ciphertext_hex: %s\n", (char *) ciphertext_hex);

ret = destroy();
if (ret) {
printf("destroy error, ret=%08x\n", ret);
}
return 0;
}

然后题目给出了一个 sdk 接口,让你自己去编译。

首先要注意一个坑点,在对消息进行加密的时候会先做一个混淆,python 表示为

1
2
3
4
5
def trans(m):
p1 = m[:8]
p2 = m[-8:]
p3 = m[8:-8]
return p1 + p2 + p3

不过这个是可逆的。

然后题目会给出两组明密文,然后要恢复一个目标密文。

不过题目中 IV 是固定的,然后又是 CFB 模式,所以我们可以算出第一组的密钥流恢复目标密文的第一块

1
2
3
4
5
6
7
8
9
10
11
12
from Crypto.Util.Padding import unpad
c1 = "949FBEA9806D720CF8FAC4AF56309A4DDC5554862D8DD597ACD6B41CCA55B8D1"
c2 = "949FBFAF806C7507F8F9C4A5543C9D4D260FC54E8492147BA230CCF78F528E9C"
c3 = '949FBFA28061720CF3A1D9FB5863C619BB8769FF8994013B98E0D6968EA7B8E08F9B0E1F44FF6C37A185E9A7892FB831BD6D33EB1F586A35CD500C3B867DA2F7'

c1 = bytes.fromhex(c1)
c3 = bytes.fromhex(c3)
m1 = b"(113.8383,34.3784)"
m2 = b"(105.9434,37.9543)"
key = xor(c1[:16], trans(m1)[:16])

print(xor(key, c3[:16]))

然后就是调用服务器密码机的SDF_Encrypt接口,对密文解密(其实这时候你会发现,好像前面不用 IV 固定先算第一块也没事)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
int main(int argc, char *argv[]) {
int ret = 0;
const unsigned char location[] = "xxxxxxxxxxxxxxxxxxxx";
const unsigned int location_len = sizeof(location) - 1;

ret = init();
if (ret) {
printf("init error, ret=%08x\n", ret);
return ret;
}

unsigned char cip_hex[1024] = "949FBFA28061720CF3A1D9FB5863C619BB8769FF8994013B98E0D6968EA7B8E08F9B0E1F44FF6C37A185E9A7892FB831BD6D33EB1F586A35CD500C3B867DA2F7";
unsigned char cip[512] = {0};
unsigned int ciphertext_hex_len = 0;
ret = hex_string_to_char_array(cip_hex, cip);
if (ret < 0) {
printf("hex_string_to_char_array error, ret=%08x\n", ret);
return ret;
}

unsigned char key[64] = {0};
unsigned int key_len = 64;
ret = SDF_Encrypt(sessionHandle, keyHandle, SGD_SM4_ECB, NULL, cip, 64, key, &key_len);

if (ret) {
printf("SDF_OpenDeviceWithPath error, ret=%08x\n", ret);
return ret;
}

for (int i=0;i<64;i++) {
printf("%02x", (unsigned int)key[i]);
}

printf("\n");

ret = destroy();
if (ret) {
printf("destroy error, ret=%08x\n", ret);
}
return 0;
}

4-OTA升级包加密

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package main

import (
"crypto/cipher"
"errors"
"github.com/tjfoc/gmsm/sm4"
)

func encrypt(key, nonce, plaintext, additionalData []byte) ([]byte, error) {
block, err := sm4.NewCipher(key)
if err != nil {
return nil, err
}

gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}

ciphertext := gcm.Seal(nil, nonce, plaintext, additionalData)
// 最终结果为:12字节nonce + 密文 + 16字节认证tag
return append(nonce, ciphertext...), nil
}

func EncryptOTAPackage(plaintextFilePath string, key, nonce []byte) (ciphertextFilePath string, err error) {
if len(key) != 16 {
return "", errors.New("invalid key")
}
if len(nonce) != 12 {
return "", errors.New("invalid nonce")
}

// 读取明文 OTA 升级包内容
// TODO 此处代码已省略

ciphertextContent, err := encrypt(key, nonce, plaintextContent, nil)
if err != nil {
return "", err
}

// 以字节流形式将加密后的升级包文件内容写到文件中
// TODO 此处代码已省略

return
}

func main() {
plainFilePath := "./files/firm-v1.0-plain.bin"
expectedCipherFilePath := "./files/firm-v1.0.bin"
key := []byte("xxxxxxxxxxxxxxxx")
nonce := []byte("xxxxxxxxxxxx")
cipherFilePath, err := EncryptOTAPackage(plainFilePath, key, nonce)
if err != nil {
panic(err)
}
if cipherFilePath != expectedCipherFilePath {
panic("invalid cipherFilePath")
}
}

然后会给出 firm-v1.0.binfirm-v1.1.bin,要求你构造一个新的 bin 文件使得实现自动驾驶功能使能位。其实就是把明文第 8 位的指定 bit 改成 1 就好了。

首先 两个给我们的文件都是密文,所以我们是读不到明文信息的。不过注意 GCM 对密文的加密就是 CTR,所以是流密码形式的,那我只要对指定 bit 做翻转就好了。然后后面就是伪造 tag 了,因为你把消息改了那 tag 肯定也不会一样了。

审计代码可以发现 GCM 的 key 和 nonce 是重用的,那这就为我们伪造 tag 提供了漏洞。我们要伪造的值是 10 块的,这里为了简单介绍,我假设两组密文都是 4 块的。并且记为 a0a_0 ~ a3,b0a_3,b_0 ~ b3b_3,然后记第一组密文 tag 为 AA,第二组密文 tag 为 BB,有

A=((((a0h+a1)h+a2)h+a3)h+l)h+s=a0h5+a1h4+a2h3+a3h2+lh+sB=((((b0h+b1)h+b2)h+b3)h+l)h+s=b0h5+b1h4+b2h3+b3h2+lh+sA=((((a_0h+a_1)h+a_2)h+a_3)h+l)h+s=a_0h^5+a_1h^4+a_2h^3+a_3h^2+lh+s\\ B=((((b_0h+b_1)h+b_2)h+b_3)h+l)h+s=b_0h^5+b_1h^4+b_2h^3+b_3h^2+lh+s\\

这里注意因为是在 GF(2128)GF(2^{128}) 下所以异或和加是等价的,然后两者相加就是两者异或变为 0,故有

A+B=(a0+b0)h5+(a1+b1)h4+(a2+b2)h3+(a3+b3)h2A+B=(a_0+b_0)h^5+(a_1+b_1)h^4+(a_2+b_2)h^3+(a_3+b_3)h^2

注意到在这个等式中我们只有 hh 是未知的,那我们可以直接解方程就好了。得到 hh 以后,那么伪造 tag 就是非常简单的事情了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
from Crypto.Util.number import *
from sage.all import *

b1 = open('firm-v1.0.bin', 'rb').read()
b2 = open('firm-v1.1.bin', 'rb').read()

nonce = b1[:12]

tag1 = b1[-16:]
tag2 = b2[-16:]

c1 = b1[12:-16]
c2 = b2[12:-16]

assert len(c1) == len(c2) == 160

t = 8
print(bin(c2[t])[2:].zfill(8))
# 01101000
ttt = 0b01101100
c3 = c2[:t] + bytes([ttt]) + c2[t+1:]

x = var('x')
R = GF(2**128, modulus=x**128 + x**7 + x**2 + x + 1, name='a')
a = R.gen()
F = PolynomialRing(R, name='x')
x = F.gen()

def to_poly(c):
return R.from_integer(int(f'{c:0128b}'[::-1], 2))

def from_poly(p):
return int(f'{p.to_integer():0128b}'[::-1], 2)

C1 = [to_poly(int.from_bytes(c1[16 * i:16 * i +16])) for i in range(len(c1) // 16)]
C2 = [to_poly(int.from_bytes(c2[16 * i:16 * i +16])) for i in range(len(c2) // 16)]

tag1 = to_poly(int.from_bytes(tag1))
tag2 = to_poly(int.from_bytes(tag2))
f = sum((C1[i] + C2[i]) * x**(len(C1) - i + 1) for i in range(len(C1))) - tag1 - tag2
H = f.roots()[0][0]

tag3 = tag2 - C2[0] * H**11 + to_poly(int.from_bytes(c3[:16])) * H**11
C3 = nonce + c3 + long_to_bytes(from_poly(tag3), 16)
print(C3)
open('firm-v1.2.bin', 'wb').write(C3)

*5-OTA服务器与OTA升级包签名

题目:

challenge_handler.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
package handlers

import (
"crypto/cipher"
"crypto/elliptic"
_ "embed"
"encoding/hex"
"errors"
"fmt"
"github.com/gin-gonic/gin"
"github.com/tjfoc/gmsm/sm2"
"github.com/tjfoc/gmsm/sm4"
"math/big"
"net/http"
)

//go:embed auth_private_key.hex
var gPrivateHex string

//go:embed auth_public_key.hex
var gPublicHex string

var errZeroParam = errors.New("zero parameter")
var one = new(big.Int).SetInt64(1)
var key []byte // SM4/GCM
var nonce []byte
var gPrivateKey *sm2.PrivateKey

func init() {
var err error

key, err = hex.DecodeString("6c87d37a658ab6b00fee969d4b107ef0")
if err != nil || len(key) != 16 {
panic(fmt.Errorf("key 配置有误"))
}
nonce, err = hex.DecodeString("17d77a33826174759e01273c")
if err != nil || len(nonce) != 12 {
panic(fmt.Errorf("nonce 配置有误"))
}

// 转换为密钥对象
gPrivateKey, _, err = HexToSM2Key(gPrivateHex, gPublicHex[2:])
if err != nil {
panic("密钥加载失败: " + err.Error())
}
}

// HexToSM2Key 十六进制字符串转密钥对象
func HexToSM2Key(privateHex, publicHex string) (*sm2.PrivateKey, *sm2.PublicKey, error) {
// 解析私钥
privateBytes, err := hex.DecodeString(privateHex)
if err != nil {
return nil, nil, fmt.Errorf("私钥解码失败: %v", err)
}
privateKey := new(sm2.PrivateKey)
privateKey.D = new(big.Int).SetBytes(privateBytes)

// 解析公钥
publicBytes, err := hex.DecodeString(publicHex)
if err != nil {
return nil, nil, fmt.Errorf("公钥解码失败: %v", err)
}

// 根据曲线参数重建公钥
curve := sm2.P256Sm2()
x := new(big.Int).SetBytes(publicBytes[:32])
y := new(big.Int).SetBytes(publicBytes[32:])
privateKey.PublicKey = sm2.PublicKey{
Curve: curve,
X: x,
Y: y,
}

return privateKey, &privateKey.PublicKey, nil
}

// ChallengeHandler 对 APP 控制端发来的挑战值用私钥进行签名并返回给 APP 控制端,APP 控制端对签名值进行验签从而确认 OTA 服务器的身份。
func ChallengeHandler(c *gin.Context) {
var req struct {
Challenge string `json:"challenge" binding:"required,min=1,max=2048"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "参数不合法"})
return
}

// 补 0
challengeHex := req.Challenge
if len(challengeHex)%2 == 1 {
challengeHex = "0" + challengeHex
}

challenge, err := hex.DecodeString(challengeHex)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "参数不合法"})
return
}

// 签名
sigHex, err := signMessage(challenge, gPrivateKey)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}

c.JSON(http.StatusOK, gin.H{"signature": sigHex})
}

func hash(data []byte) ([]byte, error) {
block, err := sm4.NewCipher(key)
if err != nil {
return nil, err
}

gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}

// func (AEAD) Seal(dst []byte, nonce []byte, plaintext []byte, additionalData []byte) []byte
authTag := gcm.Seal(nil, nonce, []byte(""), data)
return authTag, nil
}

func myRandFieldElement(c elliptic.Curve, msg, d []byte) (k *big.Int, err error) {
params := c.Params()
b := make([]byte, params.BitSize/8+8)

h, err := hash(append(msg, d...))
if err != nil {
return
}
copy(b, h)
k = new(big.Int).SetBytes(b)
n := new(big.Int).Sub(params.N, one)
k.Mod(k, n)
k.Add(k, one)
return
}

func mySm2Sign(priv *sm2.PrivateKey, msg, uid []byte) (r, s *big.Int, err error) {
digest, err := priv.PublicKey.Sm3Digest(msg, uid)
if err != nil {
return nil, nil, err
}
e := new(big.Int).SetBytes(digest)
c := priv.PublicKey.Curve
N := c.Params().N
if N.Sign() == 0 {
return nil, nil, errZeroParam
}
var k *big.Int
for {
for {
k, err = myRandFieldElement(c, msg, priv.D.Bytes())
if err != nil {
r = nil
return
}
r, _ = priv.Curve.ScalarBaseMult(k.Bytes())
r.Add(r, e)
r.Mod(r, N)
if r.Sign() != 0 {
if t := new(big.Int).Add(r, k); t.Cmp(N) != 0 {
break
}
}

}
rD := new(big.Int).Mul(priv.D, r)
s = new(big.Int).Sub(k, rD)
d1 := new(big.Int).Add(priv.D, one)
d1Inv := new(big.Int).ModInverse(d1, N)
s.Mul(s, d1Inv)
s.Mod(s, N)
if s.Sign() != 0 {
break
}
}
return
}

func signMessage(message []byte, priv *sm2.PrivateKey) (string, error) {
// 生成 SM2 签名
r, s, err := mySm2Sign(priv, message, nil)
if err != nil {
return "", err
}

// 裸签名
// 将 r 和 s 转换为字节切片
rBytes := r.Bytes()
sBytes := s.Bytes()
// 确保 r 和 s 为 32 字节
var rPadded [32]byte
var sPadded [32]byte
copy(rPadded[32-len(rBytes):], rBytes)
copy(sPadded[32-len(sBytes):], sBytes)
rHex := hex.EncodeToString(rPadded[:])
sHex := hex.EncodeToString(sPadded[:])

rawSignature := rHex + sHex
return rawSignature, nil
}

generate_auth_sign_key.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package key_generator

import (
"crypto/cipher"
"encoding/hex"
"errors"
"fmt"
"github.com/tjfoc/gmsm/sm4"
"math/big"
)

var key []byte // SM4/GCM
var nonce []byte
var one = new(big.Int).SetInt64(1)
var two = new(big.Int).SetInt64(2)

func init() {
var err error

key, err = hex.DecodeString("6c87d37a658ab6b00fee969d4b107ef0")
if err != nil || len(key) != 16 {
panic(fmt.Errorf("key 配置有误"))
}
nonce, err = hex.DecodeString("17d77a33826174759e01273c")
if err != nil || len(nonce) != 12 {
panic(fmt.Errorf("nonce 配置有误"))
}
}

func hash(data []byte) ([]byte, error) {
block, err := sm4.NewCipher(key)
if err != nil {
return nil, err
}

gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}

// func (AEAD) Seal(dst []byte, nonce []byte, plaintext []byte, additionalData []byte) []byte
authTag := gcm.Seal(nil, nonce, []byte(""), data)
return authTag, nil
}

// 生成对 OTA 升级包签名的私钥
func generateSignKey(masterKey []byte) ([]byte, error) {
if len(masterKey) != 16 {
return nil, errors.New("masterKey must be 16 bytes")
}

// 将 masterKey 转为整数
masterKeyInt := new(big.Int).SetBytes(masterKey)

// 计算 DSign 的高 128 bit
signKeyHighBytes, err := hash(masterKey)
if err != nil {
return nil, err
}

// masterKey + 1
masterKeyInt.Add(masterKeyInt, one)
// 计算 DSign 的低 128 bit
signKeyLowBytes, err := hash(masterKeyInt.Bytes())
if err != nil {
return nil, err
}

// signKeyBytes
signKeyBytes := append(signKeyHighBytes, signKeyLowBytes...)
return signKeyBytes, nil
}

// 生成 APP 控制端鉴别 OTA 服务器身份的私钥
func generateAuthKey(masterKey []byte) ([]byte, error) {
if len(masterKey) != 16 {
return nil, errors.New("masterKey must be 16 bytes")
}

// 将 masterKey 转为整数
masterKeyInt := new(big.Int).SetBytes(masterKey)

// masterKey + 2
masterKeyInt.Add(masterKeyInt, two)
// 计算 DAuth 的高 128 bit
authKeyHighBytes, err := hash(masterKeyInt.Bytes())
if err != nil {
return nil, err
}

// masterKey + 3
masterKeyInt.Add(masterKeyInt, one)
// 计算 DAuth 的低 128 bit
authKeyLowBytes, err := hash(masterKeyInt.Bytes())
if err != nil {
return nil, err
}

// authKeyBytes
authKeyBytes := append(authKeyHighBytes, authKeyLowBytes...)
return authKeyBytes, nil
}

sign_package.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package sign_package

import (
"crypto/rand"
"encoding/hex"
"fmt"
"github.com/tjfoc/gmsm/sm2"
"io/ioutil"
"math/big"
)

// LoadHexFromFile 从文件加载十六进制密钥
func LoadHexFromFile(filename string) (string, error) {
data, err := ioutil.ReadFile(filename)
if err != nil {
return "", fmt.Errorf("文件读取失败: %v", err)
}
return string(data), nil
}

// HexToSM2Key 十六进制字符串转密钥对象
func HexToSM2Key(privateHex, publicHex string) (*sm2.PrivateKey, *sm2.PublicKey, error) {
// 解析私钥
privateBytes, err := hex.DecodeString(privateHex)
if err != nil {
return nil, nil, fmt.Errorf("私钥解码失败: %v", err)
}
privateKey := new(sm2.PrivateKey)
privateKey.D = new(big.Int).SetBytes(privateBytes)

// 解析公钥
publicBytes, err := hex.DecodeString(publicHex)
if err != nil {
return nil, nil, fmt.Errorf("公钥解码失败: %v", err)
}

// 根据曲线参数重建公钥
curve := sm2.P256Sm2()
x := new(big.Int).SetBytes(publicBytes[:32])
y := new(big.Int).SetBytes(publicBytes[32:])
privateKey.PublicKey = sm2.PublicKey{
Curve: curve,
X: x,
Y: y,
}

return privateKey, &privateKey.PublicKey, nil
}

func signMessage(message []byte, priv *sm2.PrivateKey) (string, error) {
// 生成 SM2 签名
r, s, err := sm2.Sm2Sign(priv, message, nil, rand.Reader)
if err != nil {
return "", err
}

// 裸签名
// 将 r 和 s 转换为字节切片
rBytes := r.Bytes()
sBytes := s.Bytes()
// 确保 r 和 s 为 32 字节
var rPadded [32]byte
var sPadded [32]byte
copy(rPadded[32-len(rBytes):], rBytes)
copy(sPadded[32-len(sBytes):], sBytes)
rHex := hex.EncodeToString(rPadded[:])
sHex := hex.EncodeToString(sPadded[:])

rawSignature := rHex + sHex
return rawSignature, nil
}

// 对 OTA 升级包进行签名
func signPackage(filename string) string {
// 1. 读取文件内容
content, err := ioutil.ReadFile(filename)
if err != nil {
panic("读取文件内容失败: " + err.Error())
}

// 2. 加载私钥
privateHex, _ := LoadHexFromFile("sign_private_key.hex")
publicHex, _ := LoadHexFromFile("sign_public_key.hex")

// 转换为密钥对象
privateKey, _, err := HexToSM2Key(privateHex, publicHex[2:])
if err != nil {
panic("密钥加载失败: " + err.Error())
}

// 3. 签名并输出结果
sigHex, err := signMessage(content, privateKey)
if err != nil {
panic(err)
}
return sigHex
}

题目给出了一个APP 控制端的签名端口,你可以输入任意的消息进行签名,然后目标是计算出其的私钥然后回推 masterKey,最后再用 masterKey 计算出 OTA 升级包签名的私钥。

在这次比赛中,我们选择性略过了 generate_auth_sign_key.go 导致最后拿到APP 控制端的私钥后一直大眼瞪小眼,最后注意到时候已经迷迷糊糊了还是没有做出来(T_T)

首先 APP 控制端的签名算法是 SM2 签名算法,流程并没有差错,但是注意随机数的生成,是对消息和私钥进行拼接作为 GCM 的 add ,然后加密空字符,然后将生成的 tag 作为随机数。这时候思路就很明了了,只要构造两个合适的消息,使得生成的随机数一样即可计算出私钥。

我们构造两个消息,使得计算 tag 的时候可以分成两组,那么就可以写作

A1h5+A2h4+BA3h5+A4h4+BA_1h^5+A_2h^4+B\\ A_3h^5+A_4h^4+B

其中 B 为后面的值(因为都是相等的),我们需要两个等式相等,那么就随便选择一下 A1,A2,A3A_1,A_2,A_3,就可以拿到 A4A_4了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
from Crypto.Util.number import * 
import gmalg
from sage.all import *

x = var('x')
R = GF(2**128, modulus=x**128 + x**7 + x**2 + x + 1, name='a')
a = R.gen()
F = PolynomialRing(R, name='x')
x = F.gen()

def to_poly(c):
return R.from_integer(int(f'{c:0128b}'[::-1], 2))

def from_poly(p):
return int(f'{p.to_integer():0128b}'[::-1], 2)


sm4 = gmalg.SM4(b'1'*16)
key = bytes.fromhex("6c87d37a658ab6b00fee969d4b107ef0")
nonce = bytes.fromhex("17d77a33826174759e01273c")
plaintext = b"\x00"*16
cipher = gmalg.SM4(key)
ciphertext = cipher.encrypt(plaintext)
h = to_poly(int(ciphertext.hex(), 16))
ciphertext = cipher.encrypt(nonce + b'\x01'.zfill(4))
s = to_poly(int(ciphertext.hex(), 16))
x1 = b'1'*16
x1 = to_poly(bytes_to_long(x1))
x2 = x1*h
A1 = to_poly(bytes_to_long(b'1'*16))
A3 = A1 - x1
A2 = to_poly(bytes_to_long(b'1'*16))
A4 = A2 - x2
def tt(A): return long_to_bytes(from_poly(A), 16)

tar1,tar2 = tt(A1) + tt(A2), tt(A3) + tt(A4)

print(tar1.hex(), tar2.hex())

拿到两个消息之后,就可以拿到私钥了,后面就简单了对着流程逆就好了。