Есть ли более простой способ подписать XML-документ на Java?

Я пытаюсь поставить цифровую подпись на XML-документ с помощью Java. У меня есть реализация, работающая с некоторыми ссылками, которые я нашел, которые используют различные реализации в _ 1_ пакет.

Однако моя текущая реализация похожа на многие из примеры Я посмотрел - он довольно подробный и предполагает использование не менее 23 различных классов API из пакетов java.xml.crypto.dsig, javax.xml.transform и java.security, среди прочих. Такое ощущение, что я вошел на территорию factory factory factory, и это заняло мне несколько часов, чтобы понять, что происходит.

У меня вопрос: есть ли более простой способ сделать это? Если у меня есть файлы с открытым / закрытым ключом и я хочу добавить <Signature/> в XML-документ, есть ли там библиотека, которая просто позволяет мне называть что-то вроде:

OutputStream signFile(InputStream xmlFile, File privateKey)

... без всего сумасшествия XMLSignatureFactory / CanonicalizationMethod / DOMSignContext?

Я не очень хорошо разбираюсь в криптографии, и предоставляемый Java API кажется довольно сложным для разработчиков, вроде меня, пытающихся познакомиться с цифровой подписью. Если все это необходимо или в настоящее время нет более дружелюбного API, это нормально, и я готов принять это как ответ. Я просто хотел бы знать, не выбираю ли я здесь без надобности трудный путь.


person Rob Hruska    schedule 12.01.2010    source источник
comment
В качестве альтернативного решения я смог сделать однострочный системный вызов и использовать aleksey.com/xmlsec подписать XML. Я не публикую это как ответ, поскольку это выходит за рамки вопроса (т.е. в Java).   -  person Rob Hruska    schedule 22.01.2010
comment
Заводская ссылка на завод-завод не работает. Изменить: так же есть несколько других ссылок   -  person byxor    schedule 31.01.2020


Ответы (3)


Я просмотрел все варианты подписи XML-файлов и решил применить нестандартный подход. Стандарты были слишком многословными. Кроме того, мне не нужна была совместимость со стандартами - мне просто нужны были подписи на блоке XML.

Вероятно, самый простой способ «подписать» блок XML - использовать GPG с отдельной подписью.

person vy32    schedule 13.01.2010
comment
Хорошая идея, наверное, самая простая из предложенных на данный момент. Он не предлагает всех возможностей настройки javax.xml.crypto или Apache Santuario, но это действительно то, что делает другие такие сложные. - person Rob Hruska; 13.01.2010
comment
Спасибо. Одна из систем, использующих этот подход, берет блок XML, вычисляет подпись с использованием открытого ключа RSA и OpenSSL, а затем встраивает эту подпись в конец блока XML в текстовый файл. Вы можете найти код в моей программе afsign.cpp, которая является частью AFFLIB, которую можно загрузить с afflib.org - person vy32; 13.01.2010
comment
Принимаю это, потому что это более простое решение. Решение Паскаля также разумно, хотя для него по-прежнему требуется изрядное количество кода. - person Rob Hruska; 22.01.2010
comment
Хотя это просто и хорошо, это невозможно сделать в некоторых средах, где вы должны включить подпись определенным образом (например, серверы SOAP). - person Filippo Mazza; 18.08.2012
comment
Я ищу стандартный подход с использованием пакетов Java / Sun. Есть ссылки? - person kbec; 04.02.2013

Ознакомьтесь с Apache XML Security. Чтобы использовать пакет для создания и проверки подписи, ознакомьтесь с образцами в _ 1_.

person Pascal Thivent    schedule 12.01.2010
comment
Спасибо, Паскаль. Были некоторые проблемы с тем, чтобы эти примеры работали, а именно java.io.IOException: недопустимый формат хранилища ключей, хотя я смог создать свой собственный keystore.pks и заставить его работать. - person Kirby; 09.04.2013
comment
Ссылка больше не действительна - person Sireesh Yarlagadda; 02.01.2018

Создавая на основе примера 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);
        }
    }
}
person Kirby    schedule 09.04.2013
comment
Пробовал ваше предложение, однако, похоже, мне не удалось изменить алгоритм C14N с Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS на Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS. Даже когда я изменяю это в коде, выходная XML-подпись по-прежнему показывает CanonicalizationMethod как ‹CanonicalizationMethod Algorithm = w3.org/TR/2001/REC-xml-c14n-20010315 / ›. Любые идеи относительно того, что здесь может быть не так? - person mithrandir; 26.05.2014