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

Java使用PFX密钥库私钥解密W3标准XML遇密钥大小错误求助

XML解密错误:Wrong key size 解决方案

问题背景

需遵循W3标准解密XML响应,加密流程已完成,但解密时抛出密钥尺寸错误,期望解密后得到带数字签名的XML。

加密后的响应XML结构(命名空间已简化):

<xenc:EncryptedData Type="xmlenc#Element" xmlns:xenc="xmlenc#">
    <xenc:EncryptionMethod Algorithm="xmlenc#tripledes-cbc"/>
    <dsig:KeyInfo xmlns:dsig="xmldsig#">
        <xenc:EncryptedKey Recipient="name:55bb96ec-...">
            <xenc:EncryptionMethod Algorithm="xmlenc#rsa-1_5"/>
            <dsig:KeyInfo>
                <dsig:KeyName>55bb96ec-....</dsig:KeyName>
            </dsig:KeyInfo>
            <xenc:CipherData>
                <xenc:CipherValue>nO6Hic8FxebFy.....</xenc:CipherValue>
            </xenc:CipherData>
        </xenc:EncryptedKey>
    </dsig:KeyInfo>
    <xenc:CipherData>
        <xenc:CipherValue>j9v2IznqpwCunQiZG....</xenc:CipherValue>
    </xenc:CipherData>
</xenc:EncryptedData>

抛出错误:

Exception in thread "main" java.security.InvalidKeyException: Wrong key size
    at com.sun.crypto.provider.DESedeCrypt.init(DESedeCrypt.java:69)
    at com.sun.crypto.provider.CipherBlockChaining.init(CipherBlockChaining.java:93)
    at com.sun.crypto.provider.CipherCore.init(CipherCore.java:591)
    at com.sun.crypto.provider.DESedeCipher.engineInit(DESedeCipher.java:197)
    at javax.crypto.Cipher.implInit(Cipher.java:809)
    at javax.crypto.Cipher.chooseProvider(Cipher.java:867)
    at javax.crypto.Cipher.init(Cipher.java:1399)
    at javax.crypto.Cipher.init(Cipher.java:1330)
    at testDecrypt.main(testDecrypt.java:90)

现有手动解密代码:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;


//import com.sun.org.apache.xml.internal.security.utils.Base64;

public class testDecrypt {
    static {
        org.apache.xml.security.Init.init();
    }
    public static void main(String[] args) throws Exception {
        String xmlInput = "ENCRYPTED XML FOLDER\\XENC FILE.xml";
        String keyStorePW = "PFX password";
        String keyStore = "PFX FILE FOLDER\\PFX File.pfx";
        
        //build document from input xml file
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document doc = db.parse(xmlInput);
        
        //load the Private Key from Keystore
        KeyStore ks = KeyStore.getInstance("PKCS12");
        ks.load(new FileInputStream(keyStore), keyStorePW.toCharArray());
        String alias = ks.aliases().nextElement();
        PrivateKey privKey = (PrivateKey)ks.getKey(alias, keyStorePW.toCharArray());
        System.out.println("Private Key: "+privKey);
        
        //Get the encrypted Key and data element from input xml
        NodeList cipherValueNodes = doc.getElementsByTagName("xenc:CipherValue");
        Element encKey = null;
        Element encData = null;
        for(int i = 0; i < cipherValueNodes.getLength(); i++) {
            if (i == 0) {
                encKey = (Element)cipherValueNodes.item(i);
            }
            else {
                encData = (Element)cipherValueNodes.item(i);
            }
        }
        System.out.println("Encrypted key: "+encKey.getTextContent());
        System.out.println("Encrypted data: "+encData.getTextContent());
        
        //Start decryption of encrypted Key using the private key
        Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding");
        cipher.init(Cipher.DECRYPT_MODE, privKey);
        byte[] encryptedKeyBytes = Base64.getDecoder().decode(encKey.getTextContent());
        byte[] decryptedKey = cipher.doFinal(encryptedKeyBytes);
        DESedeKeySpec desEdeKeySpec = new DESedeKeySpec(decryptedKey);
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
        SecretKey key = keyFactory.generateSecret(desEdeKeySpec);
        System.out.println("Key size:"+key.getEncoded().length);
        
        //decrypt the encrypted data element using the secrek key
        int ivLen = 8;
        byte[] ivBytes = new byte[ivLen];
        byte[] encryptedDataBytes = Base64.getDecoder().decode(encData.getTextContent());
        System.arraycopy(encryptedDataBytes, 0, ivBytes, 0, ivLen);
        IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
        Cipher cipherData = Cipher.getInstance("DESede/CBC/NoPadding");
        cipherData.init(Cipher.DECRYPT_MODE, key, ivParameterSpec);
        String decryptedData = new String(cipher.doFinal(encryptedDataBytes, ivLen, encryptedDataBytes.length - ivLen));
        System.out.println("DEcrypted Data: "+decryptedData);
        
        //write the Decrypted XML
        File encryptionFile = new File("FILE OUTPUT DIRECTORY\\decryptedInfo2.xml");
        FileOutputStream f = new FileOutputStream(encryptionFile);
        TransformerFactory factory = TransformerFactory.newInstance();
        Transformer transformer = factory.newTransformer();
        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
        DOMSource source = new DOMSource(doc);
        StreamResult result = new StreamResult(f);
        transformer.transform(source, result);
        f.close();
        System.out.println(
            "Wrote document containing encrypted data to " + encryptionFile.toURI().toURL().toString()
        );
    }

}

错误原因分析

  1. RSA填充方式错误:XMLenc规范中xmlenc#rsa-1_5对应RSA/ECB/PKCS1Padding,而非代码中使用的NoPadding。错误的填充方式会导致解密出的3DES密钥长度不符合24字节要求,触发密钥尺寸错误。
  2. 手动处理IV和密文不符合规范:XMLenc的3DES-CBC加密中,IV通常会被包含在加密数据的结构中(或由算法自动处理),手动拆分前8字节作为IV的方式可能不符合实际加密时的处理逻辑。
  3. 未使用专业XMLenc库:手动解析DOM并执行加解密容易遗漏规范细节,应该用Apache Santuario(xmlsec)库直接处理整个EncryptedData元素。

修正后的解决方案

使用Apache Santuario库处理XML解密,步骤如下:

1. 添加依赖(Maven)

<dependency>
    <groupId>org.apache.santuario</groupId>
    <artifactId>xmlsec</artifactId>
    <version>2.4.3</version>
</dependency>

2. 修正后的解密代码

import org.apache.xml.security.encryption.XMLCipher;
import org.apache.xml.security.keys.KeyInfo;
import org.apache.xml.security.keys.keyresolver.KeyResolver;
import org.apache.xml.security.keys.keyresolver.implementations.RSAKeyValueResolver;
import org.apache.xml.security.keys.keyresolver.implementations.X509CertificateResolver;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

public class XmlDecryptor {
    static {
        org.apache.xml.security.Init.init();
        // 注册密钥解析器
        KeyResolver.register(new RSAKeyValueResolver());
        KeyResolver.register(new X509CertificateResolver());
    }

    public static void main(String[] args) throws Exception {
        String encryptedXmlPath = "ENCRYPTED XML FOLDER\\XENC FILE.xml";
        String pfxPath = "PFX FILE FOLDER\\PFX File.pfx";
        String pfxPassword = "PFX password";
        String outputPath = "FILE OUTPUT DIRECTORY\\decryptedInfo.xml";

        // 加载PFX证书库,获取私钥和证书
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        keyStore.load(new FileInputStream(pfxPath), pfxPassword.toCharArray());
        String alias = keyStore.aliases().nextElement();
        PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, pfxPassword.toCharArray());
        X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias);

        // 解析加密后的XML文档
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document doc = db.parse(new File(encryptedXmlPath));

        // 获取EncryptedData元素
        Element encryptedDataElement = (Element) doc.getElementsByTagNameNS(XMLCipher.XMLENC_NS, "EncryptedData").item(0);

        // 初始化XMLCipher,用于解密
        XMLCipher xmlCipher = XMLCipher.getInstance();
        xmlCipher.init(XMLCipher.DECRYPT_MODE, null);

        // 设置密钥解析器,用私钥解密EncryptedKey
        xmlCipher.setKEK(privateKey);

        // 执行解密,替换EncryptedData为原始内容
        xmlCipher.doFinal(doc, encryptedDataElement);

        // 输出解密后的XML
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        DOMSource source = new DOMSource(doc);
        StreamResult result = new StreamResult(new FileOutputStream(outputPath));
        transformer.transform(source, result);

        System.out.println("解密完成,输出文件:" + outputPath);
    }
}

代码说明

  • 直接使用XMLCipher类处理整个EncryptedData元素,自动遵循XMLenc规范解析加密算法、密钥信息
  • 通过setKEK传入私钥,自动解密EncryptedKey得到3DES密钥
  • 无需手动拆分IV和密文,库会自动处理算法细节
  • 注册密钥解析器,确保能正确识别XML中的密钥信息

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 08:30:56