有没有更简单的方法在 Java 中签署 XML 文档?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2052251/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 03:01:54  来源:igfitidea点击:

Is there an easier way to sign an XML document in Java?

javaxmlcryptographydigital-signaturexml-signature

提问by Rob Hruska

I'm trying to digitally sign an XML documentusing Java. I've got an implementation working with some references I've found that use various implementations in the javax.xml.crypto.dsigpackage.

我正在尝试使用 Java对 XML 文档进行数字签名。我有一个实现与我发现的一些参考资料一起使用,这些参考资料在javax.xml.crypto.dsig包中使用了各种实现。

However, my current implementation is like manyof the examplesI've looked at - it's rather verbose and involves using no less than 23 different API classes from the java.xml.crypto.dsig, javax.xml.transform, and java.securitypackages, among others. It feels like I've entered factory factory factoryland, and it took me several hours just to figure out what was going on.

不过,我目前的实现是像很多例子我已经看了-这是相当繁琐,包括使用不低于23个不同的API类从java.xml.crypto.dsigjavax.xml.transformjava.security包,等等。感觉就像我进入了工厂工厂工厂土地,我花了几个小时才弄清楚发生了什么。

My question is, is there an easier way to do this?If I've got public/private key files and I want to add a <Signature/>to an XML document, is there a library out there that just lets me call something like:

我的问题是,有没有更简单的方法来做到这一点?如果我有公钥/私钥文件并且我想将 a 添加<Signature/>到 XML 文档,是否有一个库可以让我调用类似的东西:

OutputStream signFile(InputStream xmlFile, File privateKey)

...without all of the XMLSignatureFactory/CanonicalizationMethod/DOMSignContext craziness?

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

I'm not very well-versed in cryptography, and the Java-provided API seems rather daunting for developers like myself trying to become familiar with digital signing. If all of this is necessary or there's currently no friendlier API out there, that's fine and I'm willing to accept that as an answer. I'd just like to know if I'm unnecessarily taking the hard road here.

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

采纳答案by vy32

I looked at all of the options for signing XML files and decided to go with a non-standard approach. The standards were all way too verbose. Also, I didn't need compatibility with the standards---I just needed signatures on a block of XML.

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

Probably the easiest way to "sign" a block of XML is to use GPG with a detached signature.

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

回答by Pascal Thivent

Have look at Apache XML Security. To use the package to generate and verify a signature, checkout the samples in src_samples/org/apache/xml/security/samples/signature/.

看看Apache XML Security。要使用该包生成和验证签名,请在src_samples/org/apache/xml/security/samples/signature/.

回答by Kirby

Building from the Apache Santuario CreateSignatureexample, the shortest thing I could come up with is this. Without the main()and its accompanying output(), it's 20 lines

从 Apache SantuarioCreateSignature示例构建,我能想到的最短的事情就是这个。没有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);
        }
    }
}