有关如何在 PFX 密钥库中使用 Java 的私钥解密 XML 的步骤

Steps on how to decrypt XML using private key in a PFX keystore with Java

提问人:juniorDev 提问时间:11/13/2023 最后编辑:juniorDev 更新时间:11/20/2023 访问量:39

问:

我们要求按照 W3 标准加密请求有效负载并解密响应。我将按照此处描述的内容进行加密。

现在需要的是解密,我发现它不像加密的反向那样直接。到目前为止,我搜索的内容讨论了对称加密和解密,并且只涉及一些 String 值。

这是响应的样子(命名空间 URL 缩短):


<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>

以下是我到目前为止遇到的以下错误。预期是解密的 XML 具有带有数字签名的实际响应结构。

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.FileInputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

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 = "XENC FILE here";
        String keyStorePW = "PFX password";
        String keyStore = "PFX File here";
        //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);

        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());

        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);

        SecretKey key = new SecretKeySpec(decryptedKey, "DESede");
        System.out.println("Key size:"+key.getEncoded().length);

        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);
      
    }

}

Java XML 加密 PFX

评论

0赞 Richard Heap 11/14/2023
据推测,您必须先解密密钥元素(使用 RSA 私钥),然后使用使用指定的对称算法 (3DES) 获得的密钥解密数据元素。此外,似乎需要将 CipherValues 以 base-64 解码为字节。
0赞 juniorDev 11/20/2023
@RichardHeap感谢您的回复。我找到了SAP博客链接,描述了您提到的内容,以及加密数据作为AES和3DES的区别。但是,我遇到了这个错误java.security.InvalidKeyException:密钥大小错误。请帮助我知道我做错了什么。
1赞 dave_thompson_085 11/20/2023
检查,它应该是 24 字节。如果没有,您的加密是错误的。decryptedKey.length
0赞 juniorDev 11/21/2023
@dave_thompson_085我设法通过添加 DESedeKeySpec 来获得正确的密钥。' DESedeKeySpec desEdeKeySpec = new DESedeKeySpec(decryptedKey);SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(“DESede”);SecretKey 密钥 = keyFactory.generateSecret(desEdeKeySpec);' 现在我在编译和运行时不再有任何错误,但我只是将我的输入作为输出取回。所以我现在不确定我可能做错了什么。
0赞 juniorDev 11/22/2023
我已经添加了获取结果xml文档,但我遇到了Document decryptedDoc = db.parse(new ByteArrayInputStream(decryptedData.getBytes("UTF-8")));[Fatal Error] :1:1: Content is not allowed in prolog.

答: 暂无答案