【问题标题】:Android: Add Digital Signature in xml Document Using DigitalSignerAndroid:使用 DigitalSigner 在 xml 文档中添加数字签名
【发布时间】:2014-08-19 18:48:08
【问题描述】:

我正在尝试使用下面的类在 xml 请求中附加数字签名,同时在 http 服务器上发布 xml。但是由于 Android 不允许我使用 javax.xml.crypto.dsig。包我无法使用它。
PN DigitalSigner 是他们希望我们用来签署 xml 的第 3 方类所以我的问题是有没有其他方法可以在不使用 javax.xml 的情况下签署 Xml 请求.crypto.dsig。如果是,那么如何..?

提前致谢

这是类:

import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.security.KeyStore;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.crypto.dsig.DigestMethod;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.SignatureMethod;
import javax.xml.crypto.dsig.SignedInfo;
import javax.xml.crypto.dsig.Transform;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
import javax.xml.crypto.dsig.keyinfo.X509Data;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;


public class DigitalSigner {

    private static final String MEC_TYPE = "DOM";
    private static final String WHOLE_DOC_URI = "";
    private static final String KEY_STORE_TYPE = "PKCS12";

    private KeyStore.PrivateKeyEntry keyEntry;

    /**
     * Constructor
     * @param keyStoreFile - Location of .p12 file
     * @param keyStorePassword - Password of .p12 file
     * @param alias - Alias of the certificate in .p12 file
     */
    public DigitalSigner(InputStream keyStoreFile, char[] keyStorePassword, String alias) {
        this.keyEntry = getKeyFromKeyStore(keyStoreFile, keyStorePassword, alias);


        if (keyEntry == null) {
            throw new RuntimeException("Key could not be read for digital signature. Please check value of signature "
                    + "alias and signature password, ");
        }
    }

    /**
     * Method to digitally sign an XML document.
     * @param xmlDocument - Input XML Document.
     * @return Signed XML document
     */
    public String signXML(String xmlDocument, boolean includeKeyInfo) {
        Security.addProvider(new BouncyCastleProvider());
        try {
            // Parse the input XML
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);
            Document inputDocument = dbf.newDocumentBuilder().parse(new InputSource(new StringReader(xmlDocument)));

            // Sign the input XML's DOM document
            Document signedDocument = sign(inputDocument, includeKeyInfo);

            // Convert the signedDocument to XML String
            StringWriter stringWriter = new StringWriter();
            TransformerFactory tf = TransformerFactory.newInstance();
            Transformer trans = tf.newTransformer();
            trans.transform(new DOMSource(signedDocument), new StreamResult(stringWriter));

            return stringWriter.getBuffer().toString();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("Error while digitally signing the XML document", e);
        }
    }

    private Document sign(Document xmlDoc, boolean includeKeyInfo) throws Exception {

        if (System.getenv("SKIP_DIGITAL_SIGNATURE") != null) {
            return xmlDoc;
        }

        // Creating the XMLSignature factory.
        XMLSignatureFactory fac = XMLSignatureFactory.getInstance(MEC_TYPE);
        // Creating the reference object, reading the whole document for
        // signing.
        Reference ref = fac.newReference(WHOLE_DOC_URI, fac.newDigestMethod(DigestMethod.SHA1, null),
                Collections.singletonList(fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)), null,
                null);

        // Create the SignedInfo.
        SignedInfo sInfo = fac.newSignedInfo(
                fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null),
                fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null), Collections.singletonList(ref));

        if (keyEntry == null) {
            throw new RuntimeException(
                    "Key could not be read for digital signature.");
        }

        X509Certificate x509Cert = (X509Certificate) keyEntry.getCertificate();

        KeyInfo kInfo = getKeyInfo(x509Cert, fac);
        DOMSignContext dsc = new DOMSignContext(this.keyEntry.getPrivateKey(), xmlDoc.getDocumentElement());
        XMLSignature signature = fac.newXMLSignature(sInfo, includeKeyInfo ? kInfo : null);
        signature.sign(dsc);

        Node node = dsc.getParent();
        return node.getOwnerDocument();

    }


    private KeyInfo getKeyInfo(X509Certificate cert, XMLSignatureFactory fac) {
        // Create the KeyInfo containing the X509Data.
        KeyInfoFactory kif = fac.getKeyInfoFactory();
        List x509Content = new ArrayList();
        x509Content.add(cert.getSubjectX500Principal().getName());
        x509Content.add(cert);
        X509Data xd = kif.newX509Data(x509Content);
        return kif.newKeyInfo(Collections.singletonList(xd));
    }

    private KeyStore.PrivateKeyEntry getKeyFromKeyStore(InputStream keyStoreFile, char[] keyStorePassword, String alias) {
        // Load the KeyStore and get the signing key and certificate.
        InputStream keyFileStream = null;
        try {
            KeyStore ks = KeyStore.getInstance(KEY_STORE_TYPE);
            keyFileStream = keyStoreFile;
            ks.load(keyFileStream, keyStorePassword);

            KeyStore.PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry) ks.getEntry(alias,
                    new KeyStore.PasswordProtection(keyStorePassword));
            return entry;

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            if (keyFileStream != null) {
                try {
                    keyFileStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

}

【问题讨论】:

    标签: android xml digital-signature


    【解决方案1】:

    试试Apache Santuario:

    Apache XML Security for Java:这个库包括标准的 JSR-105(Java XML 数字签名)API,一个成熟的基于 DOM 的 XML 签名和 XML 加密实现,以及更新的基于 StAX(流) XML 签名和 XML 加密实现。

    所以它应该兼容Java SE的数字签名实现,两者都源自JSR-105。不幸的是,独立开发实现已从 Oracle 站点中删除。

    【讨论】:

    • 注:以上平台的安全性无法保证,本人不使用(只知道存在)。
    • 我试过这样做,但是我从各自的 3rd 方供应商那里得到了数字验证错误,而且它需要更多的 jar 来包含。
    • 好吧,没有功能就无法拥有功能。我认为没有 lib 就无法做到这一点,除非您自己编程或仅从其中任何一个中集成正确的 Java 类。至于验证错误,您可能需要创建一个信任库或将证书添加到现有的。
    猜你喜欢
    • 1970-01-01
    • 2022-08-06
    • 2020-10-19
    • 2017-06-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多