You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Bouncy Castle实现CDR转ASCII及解决AIR记录ASN.1解码字段遍历问题

解决ASN.1 AIR记录遍历与CDR转ASCII问题

我看了你的代码和遇到的问题,核心是没处理好ASN.1的嵌套结构,以及对TaggedObject的理解有误。咱们一步步来解决这两个问题:

一、遍历ASN.1 AIR记录的所有字段

你的代码里有个关键错误:在获取第一个DERTaggedObject后,循环x < obj.getTagNo()完全不对——getTagNo()返回的是这个标签的编号(比如1、2这类标识字段的ID),不是元素数量,这就是你没法遍历后续字段的原因。

ASN.1结构是嵌套的,顶层Sequence里的元素可能又是TaggedObject、子Sequence等,所以必须用递归遍历来处理所有层级的字段。我给你重写了代码,加上递归遍历的逻辑:

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.bouncycastle.asn1.*;
import org.bouncycastle.util.encoders.Hex;

public class AIRDecoder {
    private ASN1InputStream asnIn;

    // 可选:根据AIR的ASN.1定义,给标签编号映射字段名称,输出更直观
    private static final Map<Integer, String> TAG_FIELD_MAP = new HashMap<>();
    static {
        TAG_FIELD_MAP.put(1, "AccountID");
        TAG_FIELD_MAP.put(2, "RechargeAmount");
        TAG_FIELD_MAP.put(3, "RechargeTimestamp");
        // 按你的AIR补充其他标签对应的字段名
    }

    public void decodeAIR(byte[] rawData) throws IOException {
        asnIn = new ASN1InputStream(new ByteArrayInputStream(rawData));
        ASN1Primitive topObj = asnIn.readObject();

        // AIR记录顶层一般是TaggedObject,先解析它
        if (topObj instanceof ASN1TaggedObject) {
            ASN1TaggedObject airRecord = (ASN1TaggedObject) topObj;
            System.out.println("=== AIR Record Start ===");
            traverseASN1Element(airRecord.getObject(), 0);
            System.out.println("=== AIR Record End ===");
        } else {
            System.err.println("Invalid AIR record format: top element is not a TaggedObject");
        }
    }

    // 递归遍历所有ASN.1元素的核心方法
    private void traverseASN1Element(ASN1Encodable element, int indent) {
        String indentStr = "  ".repeat(indent);

        if (element instanceof ASN1Sequence) {
            ASN1Sequence sequence = (ASN1Sequence) element;
            System.out.printf("%s[SEQUENCE] (包含 %d 个元素)%n", indentStr, sequence.size());
            for (int i = 0; i < sequence.size(); i++) {
                System.out.printf("%s  第 %d 个元素: ", indentStr, i+1);
                traverseASN1Element(sequence.getObjectAt(i), indent + 2);
            }
        } else if (element instanceof ASN1TaggedObject) {
            ASN1TaggedObject taggedObj = (ASN1TaggedObject) element;
            int tagNo = taggedObj.getTagNo();
            String fieldName = TAG_FIELD_MAP.getOrDefault(tagNo, "未知字段");
            System.out.printf("%s[标签字段] 编号: %d (%s), 显式标签: %b%n", indentStr, tagNo, fieldName, taggedObj.isExplicit());
            // 递归解析标签内的实际内容
            traverseASN1Element(taggedObj.getObject(), indent + 2);
        } else {
            // 处理基本类型(字符串、整数、二进制等)
            ASN1Primitive primitive = element.toASN1Primitive();
            System.out.printf("%s[基础类型] %s%n", indentStr, primitive.getClass().getSimpleName());
            
            // 尝试转换为ASCII可读格式
            if (primitive instanceof ASN1UTF8String) {
                String asciiVal = ((ASN1UTF8String) primitive).getString();
                System.out.printf("%s  ASCII值: %s%n", indentStr, asciiVal);
            } else if (primitive instanceof ASN1Integer) {
                long intVal = ((ASN1Integer) primitive).getValue().longValue();
                System.out.printf("%s  数值: %d%n", indentStr, intVal);
            } else if (primitive instanceof ASN1OctetString) {
                byte[] octetBytes = ((ASN1OctetString) primitive).getOctets();
                try {
                    String asciiVal = new String(octetBytes, "US-ASCII");
                    System.out.printf("%s  ASCII值(二进制转): %s%n", indentStr, asciiVal);
                } catch (Exception e) {
                    // 转ASCII失败就输出十六进制
                    System.out.printf("%s  十六进制值: %s%n", indentStr, Hex.toHexString(octetBytes));
                }
            }
        }
    }
}

代码说明:

  • 新增traverseASN1Element递归方法,能处理所有ASN.1类型(Sequence、TaggedObject、基础类型),不管嵌套多少层都能遍历到。
  • 加入了标签到字段名的映射,输出更易读;对不同基础类型做了针对性的ASCII转换。

二、将CDR转换为ASCII格式

CDR转ASCII本质就是把每个ASN.1字段的内容解析后,以可读的ASCII格式输出,上面的代码已经实现了核心逻辑。如果需要更规整的结构化ASCII输出(比如键值对或JSON),可以在递归遍历的时候收集字段数据,最后统一输出:

比如添加一个Map<String, Object>来存储解析后的字段:

private Map<String, Object> cdrData = new HashMap<>();

// 在traverseASN1Element处理TaggedObject时,把字段值存入map
if (primitive instanceof ASN1UTF8String) {
    String asciiVal = ((ASN1UTF8String) primitive).getString();
    cdrData.put(fieldName, asciiVal);
} else if (primitive instanceof ASN1Integer) {
    long intVal = ((ASN1Integer) primitive).getValue().longValue();
    cdrData.put(fieldName, intVal);
}

最后直接输出这个map的ASCII格式:

System.out.println("=== CDR ASCII格式 ===");
cdrData.forEach((key, value) -> System.out.printf("%s: %s%n", key, value));

这样就能得到清晰的键值对格式ASCII输出。

内容的提问来源于stack exchange,提问作者wisam shaheen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 14:17:55