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

Java转C#:APDU安全消息解密迁移遇MAC验证及900状态问题

APDU安全消息解密的C#迁移实现(修复填充与状态字问题)

问题分析

当前代码缺少ISO 9797-1方法2的去填充逻辑,且状态字解析错误,导致解密后返回异常的900状态。以下是对齐open-ecard Java原逻辑的完整C#实现。

完整修正代码

using System;
using System.Security.Cryptography;
using System.Linq;

public class SecureMessaging
{
    public byte[] DecryptSecureMessaging(byte[] encryptedData, byte[] sessionKey, byte[] iv)
    {
        if (encryptedData == null || encryptedData.Length == 0)
            throw new ArgumentNullException(nameof(encryptedData));
        // 适配3DES(PACE默认),若用AES则修改密钥长度校验为16/24/32
        if (sessionKey == null || sessionKey.Length != 24)
            throw new ArgumentException("Invalid 3DES session key length (must be 24 bytes)", nameof(sessionKey));
        if (iv == null || iv.Length != 8)
            throw new ArgumentException("Invalid IV length for 3DES (must be 8 bytes)", nameof(iv));

        byte[] decryptedRaw;
        // 对齐Java原代码的DESede/CBC/NoPadding配置
        using (var des3 = TripleDES.Create())
        {
            des3.Key = sessionKey;
            des3.IV = iv;
            des3.Mode = CipherMode.CBC;
            des3.Padding = PaddingMode.None;

            var decryptor = des3.CreateDecryptor();
            decryptedRaw = decryptor.TransformFinalBlock(encryptedData, 0, encryptedData.Length);
        }

        // 移除ISO 9797-1方法2填充
        var decryptedData = RemoveIso97971Padding(decryptedRaw);

        // 分离响应数据与状态字(APDU安全消息最后2字节为状态字)
        if (decryptedData.Length < 2)
            throw new InvalidOperationException("Decrypted data too short to contain status word");

        var statusWord = decryptedData.Skip(decryptedData.Length - 2).Take(2).ToArray();
        var responseData = decryptedData.Take(decryptedData.Length - 2).ToArray();

        // 验证正常状态字0x9000(原问题中900是错误解析导致)
        if (statusWord[0] != 0x90 || statusWord[1] != 0x00)
            throw new InvalidOperationException($"Operation failed with status: 0x{statusWord[0]:X2}{statusWord[1]:X2}");

        return responseData;
    }

    // 实现ISO 9797-1方法2去填充
    private byte[] RemoveIso97971Padding(byte[] data)
    {
        if (data.Length == 0) return data;

        int padLength = data[data.Length - 1];
        // 校验填充合法性:所有填充字节必须等于填充长度
        for (int i = data.Length - padLength; i < data.Length; i++)
        {
            if (data[i] != padLength)
                throw new CryptographicException("Invalid ISO 9797-1 padding");
        }

        return data.Take(data.Length - padLength).ToArray();
    }

    public static void Main()
    {
        // 替换为实际测试数据
        var encrypted = Convert.FromHexString("YOUR_ENCRYPTED_APDU_DATA");
        var key = Convert.FromHexString("YOUR_24BYTE_3DES_KEY");
        var iv = Convert.FromHexString("YOUR_8BYTE_IV");

        var sm = new SecureMessaging();
        try
        {
            var result = sm.DecryptSecureMessaging(encrypted, key, iv);
            Console.WriteLine($"Decrypted Data: {Convert.ToHexString(result)}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
        }
    }
}

核心修正说明

  1. 算法对齐:严格匹配Java原代码的DESede/CBC/NoPadding配置,C#中禁用自动填充,手动处理填充逻辑。
  2. 填充逻辑补全:实现了ISO 9797-1方法2的去填充,这是代码中缺失的核心部分,确保解密后数据还原为原始内容。
  3. 状态字修复:原问题中返回900是因为错误解析了状态字(正确的成功状态是0x9000,即十进制36864),修正后从解密数据末尾提取2字节作为状态字并验证。
  4. 参数校验:增加密钥、IV长度校验,避免因参数不匹配导致的解密失败。

排查建议

  • 若状态字仍异常,检查会话密钥、IV与Java端是否完全一致(字节顺序、长度)
  • 若填充校验失败,确认加密端是否使用ISO 9797-1方法2填充,或调整去填充逻辑匹配实际规则
  • 若使用AES而非3DES,替换TripleDES为Aes类,并调整密钥(16/24/32字节)和IV(16字节)的长度校验

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 12:28:14