如何从Scapy解析的SSL/TLS ServerHello包中解码ASN1证书字段?
我之前处理SSL/TLS证书解析的时候也碰到过这个问题,给你几个实用的解决办法,既能保留Scapy对数据包头部的处理能力,又能解析那些ASN.1编码的字段:
方案1:用Scapy原生ASN1对象的属性直接提取值
Scapy的ASN.1类型(比如ASN1_UTC_TIME、ASN1_OID、ASN1_NULL)其实都内置了访问原始值的属性,不需要额外工具就能直接获取:
from scapy.layers.ssl_tls import TLS from scapy.all import rdpcap from datetime import datetime # 读取PCAP文件 packets = rdpcap("your_capture.pcap") for pkt in packets: # 筛选ServerHello握手包 if TLS in pkt and pkt[TLS].type == 22: # TLS握手消息类型 handshake_msg = pkt[TLS].msg[0] if handshake_msg.msgtype == 2: # ServerHello消息 # 提取第一个证书(如果有多个证书的话可以遍历certs列表) server_cert = handshake_msg.certs[0] # 解析有效期起始时间(ASN1_UTC_TIME) not_before = server_cert.tbs_certificate.validity.not_before print(f"原始UTC时间字符串: {not_before.val}") # 转成Python datetime对象 not_before_dt = datetime.strptime(not_before.val, "%y%m%d%H%M%SZ") print(f"格式化后的起始时间: {not_before_dt}") # 解析签名算法OID(ASN1_OID) sign_algo_oid = server_cert.tbs_certificate.signature.oid print(f"签名算法OID: {sign_algo_oid.val}") # 解析签名值(ASN1_NULL) sign_value = server_cert.signature_value if isinstance(sign_value, ASN1_NULL): print(f"签名值类型为NULL,原始值: {sign_value.val}")
说明:
- 对于
ASN1_UTC_TIME,直接访问.val就能拿到类似170321131500Z的原始字符串,再用datetime.strptime格式化即可。 - 对于
ASN1_OID,.val属性会返回完整的OID字符串(比如.1.2.840.113549.1.1.11)。 - 对于
ASN1_NULL,.val会返回其底层的原始值(比如0L)。
方案2:将Scapy ASN1对象转字节后用asn1crypto解析
如果需要更复杂的ASN.1解析(比如处理嵌套结构),可以把Scapy的ASN1对象转成DER编码的字节流,再用asn1crypto处理:
from scapy.layers.ssl_tls import TLS from scapy.all import rdpcap from scapy.asn1.asn1 import ASN1_UTC_TIME, ASN1_OID from asn1crypto.core import UTCTime, ObjectIdentifier packets = rdpcap("your_capture.pcap") for pkt in packets: if TLS in pkt and pkt[TLS].type == 22 and pkt[TLS].msg[0].msgtype == 2: server_cert = pkt[TLS].msg[0].certs[0] # 处理UTC时间 not_before_asn1 = server_cert.tbs_certificate.validity.not_before # 把Scapy ASN1对象转成字节 not_before_bytes = raw(not_before_asn1) # 用asn1crypto解析 utc_time_obj = UTCTime.load(not_before_bytes) print(f"asn1crypto解析的起始时间: {utc_time_obj.native}") # 处理签名算法OID sign_algo_asn1 = server_cert.tbs_certificate.signature.oid sign_algo_bytes = raw(sign_algo_asn1) oid_obj = ObjectIdentifier.load(sign_algo_bytes) print(f"asn1crypto解析的OID: {oid_obj.native}")
说明:
- Scapy的
raw()函数可以将任何ASN1对象序列化成标准DER字节,解决了你之前遇到的TypeError问题。 asn1crypto的.native属性会自动将ASN.1类型转成对应的Python原生类型(比如UTCTime转成datetime,OID转成字符串)。
方案3:借助Scapy的X509模块直接解析证书
如果只需要解析证书的常规字段,Scapy的X509模块可以直接处理DER格式的证书,省去手动处理ASN.1的麻烦:
from scapy.layers.ssl_tls import TLS from scapy.all import rdpcap from scapy.layers.x509 import X509 packets = rdpcap("your_capture.pcap") for pkt in packets: if TLS in pkt and pkt[TLS].type == 22 and pkt[TLS].msg[0].msgtype == 2: server_cert = pkt[TLS].msg[0].certs[0] # 提取证书的DER字节 cert_der = raw(server_cert) # 用Scapy X509解析 x509_cert = X509(cert_der) # 直接获取解析后的字段 print(f"证书起始有效期: {x509_cert.get_not_before()}") print(f"证书签名算法: {x509_cert.get_signature_algorithm()}") print(f"证书颁发者: {x509_cert.get_issuer()}")
说明:
X509类封装了证书的常用解析方法,比如get_not_before()、get_signature_algorithm(),直接调用就能拿到格式化后的值,非常省心。- 这个方案最适合只需要提取证书常规字段的场景,不需要关心底层ASN.1结构。
内容的提问来源于stack exchange,提问作者Sadia Bashir




