You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何从Scapy解析的SSL/TLS ServerHello包中解码ASN1证书字段?

我之前处理SSL/TLS证书解析的时候也碰到过这个问题,给你几个实用的解决办法,既能保留Scapy对数据包头部的处理能力,又能解析那些ASN.1编码的字段:

方案1:用Scapy原生ASN1对象的属性直接提取值

Scapy的ASN.1类型(比如ASN1_UTC_TIMEASN1_OIDASN1_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

火山引擎 最新活动