【问题标题】:Is there an easier way to sign an XML document in Java?有没有更简单的方法在 Java 中签署 XML 文档?
【发布时间】:2011-01-04 08:48:08
【问题描述】:

我正在尝试使用 Java digitally sign an XML document。我有一个使用我发现的一些参考的实现,这些参考在javax.xml.crypto.dsig 包中使用了各种实现。

但是,我当前的实现就像我看过的 examples 中的 many - 它相当冗长,涉及使用来自 java.xml.crypto.dsigjavax.xml.transform 和 @987654330 的不少于 23 个不同的 API 类@ 包等。感觉就像我进入了factory factory factory 土地,我花了几个小时才弄清楚发生了什么。

我的问题是,有更简单的方法吗?如果我有公钥/私钥文件并且我想在 XML 文档中添加 <Signature/>,有那里的图书馆让我可以调用类似的东西:

OutputStream signFile(InputStream xmlFile, File privateKey)

...没有所有的 XMLSignatureFactory/CanonicalizationMethod/DOMSignContext 疯狂?

我不是很精通密码学,Java 提供的 API 对于像我这样试图熟悉数字签名的开发人员来说似乎相当令人生畏。如果所有这些都是必要的,或者目前没有更友好的 API,那很好,我愿意接受它作为答案。我只是想知道我是否在这里不必要地采取了艰难的道路。

【问题讨论】:

  • 作为替代解决方案,我能够进行单行系统调用并使用aleksey.com/xmlsec 对 XML 进行签名。我没有将此作为答案发布,因为它不在问题的范围内(即“在 Java 中”)。
  • “工厂工厂工厂”链接已损坏。编辑:其他一些链接也是如此

标签: java xml cryptography digital-signature xml-signature


【解决方案1】:

从 Apache Santuario CreateSignature 示例构建,我能想到的最短的就是这个。如果没有 main() 及其随附的 output(),则为 20 行

import java.io.*;
import java.security.Key;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.commons.io.IOUtils;
import org.apache.xml.security.Init;
import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xml.security.signature.XMLSignature;
import org.apache.xml.security.transforms.Transforms;
import org.apache.xml.security.utils.Constants;
import org.apache.xml.security.utils.ElementProxy;
import org.w3c.dom.Document;

public class CreateSignature {

    private static final String PRIVATE_KEY_ALIAS = "test-alias";
    private static final String PRIVATE_KEY_PASS = "test";
    private static final String KEY_STORE_PASS = "test";
    private static final String KEY_STORE_TYPE = "JKS";

    public static void main(String... unused) throws Exception {
        final InputStream fileInputStream = new FileInputStream("test.xml");
        try {
            output(signFile(fileInputStream, new File("keystore.jks")), "signed-test.xml");
        }
        finally {
            IOUtils.closeQuietly(fileInputStream);
        }
    }

    public static ByteArrayOutputStream signFile(InputStream xmlFile, File privateKeyFile) throws Exception {
        final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(xmlFile);
        Init.init();
        ElementProxy.setDefaultPrefix(Constants.SignatureSpecNS, "");
        final KeyStore keyStore = loadKeyStore(privateKeyFile);
        final XMLSignature sig = new XMLSignature(doc, null, XMLSignature.ALGO_ID_SIGNATURE_RSA);
        final Transforms transforms = new Transforms(doc);
        transforms.addTransform(Transforms.TRANSFORM_ENVELOPED_SIGNATURE);
        sig.addDocument("", transforms, Constants.ALGO_ID_DIGEST_SHA1);
        final Key privateKey = keyStore.getKey(PRIVATE_KEY_ALIAS, PRIVATE_KEY_PASS.toCharArray());
        final X509Certificate cert = (X509Certificate)keyStore.getCertificate(PRIVATE_KEY_ALIAS);
        sig.addKeyInfo(cert);
        sig.addKeyInfo(cert.getPublicKey());
        sig.sign(privateKey);
        doc.getDocumentElement().appendChild(sig.getElement());
        final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        outputStream.write(Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS).canonicalizeSubtree(doc));
        return outputStream;
    }

    private static KeyStore loadKeyStore(File privateKeyFile) throws Exception {
        final InputStream fileInputStream = new FileInputStream(privateKeyFile);
        try {
            final KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE);
            keyStore.load(fileInputStream, KEY_STORE_PASS.toCharArray());
            return keyStore;
        }
        finally {
            IOUtils.closeQuietly(fileInputStream);
        }
    }

    private static void output(ByteArrayOutputStream signedOutputStream, String fileName) throws IOException {
        final OutputStream fileOutputStream = new FileOutputStream(fileName);
        try {
            fileOutputStream.write(signedOutputStream.toByteArray());
            fileOutputStream.flush();
        }
        finally {
            IOUtils.closeQuietly(fileOutputStream);
        }
    }
}

【讨论】:

  • 正在尝试您的建议,但是我似乎无法将 C14N 算法从 Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS 更改为 Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS。即使我在代码中更改它,输出 XML 签名仍将 CanonicalizationMethod 显示为 w3.org/TR/2001/REC-xml-c14n-20010315" />。关于这里可能出现什么问题的任何想法?
【解决方案2】:

看看Apache XML Security。要使用包生成和验证签名,请查看src_samples/org/apache/xml/security/samples/signature/ 中的示例。

【讨论】:

  • 谢谢你,帕斯卡。在让这些示例正常工作时遇到了一些麻烦,即“java.io.IOException: Invalid keystore format”,尽管我能够创建自己的 keystore.pks 并使其正常工作。
  • 链接不再有效
【解决方案3】:

我查看了签署 XML 文件的所有选项,并决定采用非标准方法。这些标准都太冗长了。另外,我不需要与标准兼容——我只需要一个 XML 块上的签名。

“签名”XML 块的最简单方法可能是使用带有分离签名的 GPG。

【讨论】:

  • 好主意,可能是迄今为止提供的最简单的一个。它没有提供 javax.xml.crypto 或 Apache 的 Santuario 的所有可配置性,但这确实是其他那些如此复杂的原因。
  • 谢谢。采用这种方法的一个系统采用 XML 块,使用 RSA 公钥和 OpenSSL 计算签名,然后将该签名嵌入到文本文件中 XML 块的末尾。您可以在我的 afsign.cpp 程序中找到代码,它是 AFFLIB 的一部分,可以从 afflib.org 下载
  • 接受这个,因为它是更简单的解决方案。 Pascal 的解决方案也是合理的,尽管它仍然需要相当多的代码。
  • 虽然简单好用,但在某些环境中无法做到这一点,您必须以特定方式包含签名(如 SOAP 服务器)
  • 我正在寻找一种使用 Java/Sun 包的标准方法。有链接吗?
猜你喜欢
  • 2010-12-15
  • 1970-01-01
  • 1970-01-01
  • 2020-10-26
  • 1970-01-01
  • 1970-01-01
  • 2023-02-21
  • 1970-01-01
  • 2014-03-29
相关资源
最近更新 更多