Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/Java/Openjdk/test/lib/jdk/test/lib/security/   (Sun/Oracle ©)  Datei vom 13.11.2022 mit Größe 22 kB image not shown  

Quelle  XMLUtils.java   Sprache: JAVA

 
/*
 * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */


package jdk.test.lib.security;

import jdk.test.lib.Asserts;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import javax.xml.crypto.*;
import javax.xml.crypto.dom.DOMStructure;
import javax.xml.crypto.dsig.*;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.dsig.keyinfo.*;
import javax.xml.crypto.dsig.spec.*;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import java.io.File;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.*;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAKey;
import java.security.spec.PSSParameterSpec;
import java.util.*;

// A collection of test utility methods for parsing, validating and
// generating XML Signatures.
public class XMLUtils {

    private static final XMLSignatureFactory FAC =
            XMLSignatureFactory.getInstance("DOM");

    //////////// MAIN as TEST ////////////
    public static void main(String[] args) throws Exception {
        var x = "cx";
        var p = Files.write(Path.of("x.xml"), List.of(x));
        var b = Path.of("").toUri().toString();
        var d = string2doc(x);
        // keytool -keystore ks -keyalg ec -storepass changeit -genkeypair -alias a -dname CN=a
        var pass = "changeit".toCharArray();
        var ks = KeyStore.getInstance(new File("ks"), pass);
        var c = (X509Certificate) ks.getCertificate("a");
        var pr = (PrivateKey) ks.getKey("a", pass);
        var pu = c.getPublicKey();
        var s0 = signer(pr); // No KeyInfo
        var s1 = signer(pr, pu); // KeyInfo is PublicKey
        var s2 = signer(pr, c); // KeyInfo is X509Data
        var s3 = signer(ks, "a", pass); // KeyInfo is KeyName
        var v1 = validator(); // knows nothing
        var v2 = validator(ks); // knows KeyName
        Asserts.assertTrue(v1.validate(s0.sign(d), pu)); // need PublicKey
        Asserts.assertTrue(v1.validate(s1.sign(d))); // can read KeyInfo
        Asserts.assertTrue(v1.validate(s2.sign(d))); // can read KeyInfo
        Asserts.assertTrue(v2.validate(s3.sign(d))); // can read KeyInfo
        Asserts.assertTrue(v2.secureValidation(false).validate(s3.sign(p.toUri()))); // can read KeyInfo
        Asserts.assertTrue(v2.secureValidation(false).baseURI(b).validate(
                s3.sign(p.toAbsolutePath().getParent().toUri(), p.getFileName().toUri()))); // can read KeyInfo
        Asserts.assertTrue(v1.validate(s1.sign("text"))); // plain text
        Asserts.assertTrue(v1.validate(s1.sign("binary".getBytes()))); // raw data
        Asserts.assertTrue(v1.validate(s1.signEnveloping(d, "x""#x")));
        Asserts.assertTrue(v1.validate(s1.signEnveloping(d, "x""#xpointer(id('x'))")));
    }

    //////////// CONVERT ////////////

    // Converts a Document object to string
    public static String doc2string(Document doc) throws Exception {
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
        // Indentation would invalidate the signature
        // transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        StringWriter writer = new StringWriter();
        transformer.transform(new DOMSource(doc), new StreamResult(writer));
        return writer.getBuffer().toString();
    }

    // Parses a string into a Document object
    public static Document string2doc(String input) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        return factory.newDocumentBuilder().
                parse(new InputSource(new StringReader(input)));
    }

    // Clones a document
    public static Document clone(Document d) throws Exception {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document copiedDocument = db.newDocument();
        Node copiedRoot = copiedDocument.importNode(d.getDocumentElement(), true);
        copiedDocument.appendChild(copiedRoot);
        return copiedDocument;
    }

    //////////// QUERY AND EDIT ////////////

    // An XPath object that recognizes ds and pss names
    public static final XPath XPATH;

    static {
        XPATH = XPathFactory.newInstance().newXPath();
        XPATH.setNamespaceContext(new NamespaceContext() {
            @Override
            public String getNamespaceURI(String prefix) {
                return switch (prefix) {
                    case "ds" -> "http://www.w3.org/2000/09/xmldsig#";
                    case "pss" -> "http://www.w3.org/2007/05/xmldsig-more#";
                    default -> throw new IllegalArgumentException();
                };
            }

            @Override
            public String getPrefix(String namespaceURI) {
                return null;
            }

            @Override
            public Iterator<String> getPrefixes(String namespaceURI) {
                return null;
            }
        });
    }

    // Returns an Element inside a Document
    public static Element sub(Document d, String path) throws Exception {
        return (Element) XMLUtils.XPATH.evaluate(path, d, XPathConstants.NODE);
    }

    // Returns a new document with an attribute modified
    public static Document withAttribute(Document d, String path, String attr, String value) throws Exception {
        d = clone(d);
        sub(d, path).setAttribute(attr, value);
        return d;
    }

    // Returns a new document with a text modified
    public static Document withText(Document d, String path, String value) throws Exception {
        d = clone(d);
        sub(d, path).setTextContent(value);
        return d;
    }

    // Returns a new document without a child element
    public static Document withoutNode(Document d, String... paths) throws Exception {
        d = clone(d);
        for (String path : paths) {
            Element e = sub(d, path);
            e.getParentNode().removeChild(e);
        }
        return d;
    }

    //////////// SIGN ////////////

    // Creates a signer from a private key and a certificate
    public static Signer signer(PrivateKey privateKey, X509Certificate cert)
            throws Exception {
        return signer(privateKey).cert(cert);
    }

    // Creates a signer from a private key and a public key
    public static Signer signer(PrivateKey privateKey, PublicKey publicKey)
            throws Exception {
        return signer(privateKey).publicKey(publicKey);
    }

    // Creates a signer from a private key entry in a keystore. The KeyInfo
    // inside the signature will only contain a KeyName to alias
    public static Signer signer(KeyStore ks, String alias, char[] password)
            throws Exception {
        return signer((PrivateKey) ks.getKey(alias, password))
                .keyName(alias);
    }

    // Creates a signer from a private key. There will be no KeyInfo inside
    // the signature and must be validated with a given public key.
    public static Signer signer(PrivateKey privateKey)
            throws Exception {
        return new Signer(privateKey);
    }

    public static class Signer {

        PrivateKey privateKey;  // signer key, never null
        X509Certificate cert;   // certificate, optional
        PublicKey publicKey;    // public key, optional
        String keyName;         // alias, optional

        SignatureMethod sm;     // default determined by privateKey
        DigestMethod dm;        // default SHA-256
        CanonicalizationMethod cm;  // default EXCLUSIVE
        Transform tr;           // default ENVELOPED

        public Signer(PrivateKey privateKey) throws Exception {
            this.privateKey = privateKey;
            dm(DigestMethod.SHA256);
            tr(Transform.ENVELOPED);
            cm(CanonicalizationMethod.EXCLUSIVE);
            String alg = privateKey.getAlgorithm();
            if (alg.equals("RSASSA-PSS")) {
                PSSParameterSpec pspec
                        = (PSSParameterSpec) ((RSAKey) privateKey).getParams();
                if (pspec != null) {
                    sm(SignatureMethod.RSA_PSS, new RSAPSSParameterSpec(pspec));
                } else {
                    sm(SignatureMethod.RSA_PSS);
                }
            } else {
                sm(switch (privateKey.getAlgorithm()) {
                    case "RSA" -> SignatureMethod.RSA_SHA256;
                    case "DSA" -> SignatureMethod.DSA_SHA256;
                    case "EC" -> SignatureMethod.ECDSA_SHA256;
                    default -> throw new InvalidKeyException();
                });
            }
        }

        // Change KeyInfo source

        public Signer cert(X509Certificate cert) {
            this.cert = cert;
            return this;
        }

        public Signer publicKey(PublicKey key) {
            this.publicKey = key;
            return this;
        }

        public Signer keyName(String n) {
            keyName = n;
            return this;
        }

        // Change various methods

        public Signer tr(String transform) throws Exception {
            TransformParameterSpec params = null;
            switch (transform) {
                case Transform.XPATH:
                    params = new XPathFilterParameterSpec("//.");
                    break;
                case Transform.XPATH2:
                    params = new XPathFilter2ParameterSpec(
                            Collections.singletonList(new XPathType("//.",
                                    XPathType.Filter.INTERSECT)));
                    break;
            }
            tr = FAC.newTransform(transform, params);
            return this;
        }

        public Signer sm(String method) throws Exception {
            sm = FAC.newSignatureMethod(method, null);
            return this;
        }

        public Signer dm(String method) throws Exception {
            dm = FAC.newDigestMethod(method, null);
            return this;
        }

        public Signer cm(String method) throws Exception {
            cm = FAC.newCanonicalizationMethod(method, (C14NMethodParameterSpec) null);
            return this;
        }

        public Signer sm(String method, SignatureMethodParameterSpec spec)
                throws Exception {
            sm = FAC.newSignatureMethod(method, spec);
            return this;
        }

        public Signer dm(String method, DigestMethodParameterSpec spec)
                throws Exception {
            dm = FAC.newDigestMethod(method, spec);
            return this;
        }

        // Signs different sources

        // Signs an XML file in detached mode
        public Document sign(URI uri) throws Exception {
            Document newDocument = DocumentBuilderFactory.newInstance()
                    .newDocumentBuilder().newDocument();
            FAC.newXMLSignature(buildSignedInfo(uri.toString()), buildKeyInfo()).sign(
                    new DOMSignContext(privateKey, newDocument));
            return newDocument;
        }

        // Signs an XML file in a relative reference in detached mode
        public Document sign(URI base, URI ref) throws Exception {
            Document newDocument = DocumentBuilderFactory.newInstance()
                    .newDocumentBuilder().newDocument();
            DOMSignContext ctxt = new DOMSignContext(privateKey, newDocument);
            ctxt.setBaseURI(base.toString());
            FAC.newXMLSignature(buildSignedInfo(ref.toString()), buildKeyInfo()).sign(ctxt);
            return newDocument;
        }

        // Signs a document in enveloped mode
        public Document sign(Document document) throws Exception {
            DOMResult result = new DOMResult();
            TransformerFactory.newInstance().newTransformer()
                    .transform(new DOMSource(document), result);
            Document newDocument = (Document) result.getNode();
            FAC.newXMLSignature(buildSignedInfo(""), buildKeyInfo()).sign(
                    new DOMSignContext(privateKey, newDocument.getDocumentElement()));
            return newDocument;
        }

        // Signs a document in enveloping mode
        public Document signEnveloping(Document document, String id, String ref) throws Exception {
            Document newDocument = DocumentBuilderFactory.newInstance()
                    .newDocumentBuilder().newDocument();
            FAC.newXMLSignature(
                    buildSignedInfo(FAC.newReference(ref, dm)),
                    buildKeyInfo(),
                    List.of(FAC.newXMLObject(List.of(new DOMStructure(document.getDocumentElement())),
                            id, nullnull)),
                    null,
                    null)
                    .sign(new DOMSignContext(privateKey, newDocument));
            return newDocument;
        }

        // Signs a raw byte array
        public Document sign(byte[] data) throws Exception {
            Document newDocument = DocumentBuilderFactory.newInstance()
                    .newDocumentBuilder().newDocument();
            FAC.newXMLSignature(
                    buildSignedInfo(FAC.newReference("#object", dm, List.of
                            (FAC.newTransform(Transform.BASE64,
                                    (TransformParameterSpec) null)), nullnull)),
                    buildKeyInfo(),
                    List.of(FAC.newXMLObject(List.of(new DOMStructure(
                            newDocument.createTextNode(Base64.getEncoder().encodeToString(data)))),
                            "object"nullnull)),
                    null,
                    null)
                    .sign(new DOMSignContext(privateKey, newDocument));
            return newDocument;
        }

        // Signs a plain string
        public Document sign(String str) throws Exception {
            Document newDocument = DocumentBuilderFactory.newInstance()
                    .newDocumentBuilder().newDocument();
            FAC.newXMLSignature(
                    buildSignedInfo(FAC.newReference("#object", dm)),
                    buildKeyInfo(),
                    List.of(FAC.newXMLObject(List.of(new DOMStructure(newDocument.createTextNode(str))),
                            "object"nullnull)),
                    null,
                    null)
                    .sign(new DOMSignContext(privateKey, newDocument));
            return newDocument;
        }

        // Builds a SignedInfo for a string reference
        private SignedInfo buildSignedInfo(String ref) {
            return FAC.newSignedInfo(
                    cm,
                    sm,
                    List.of(FAC.newReference(
                            ref,
                            dm,
                            List.of(tr),
                            nullnull)));
        }

        // Builds a SignedInfo for a Reference
        private SignedInfo buildSignedInfo(Reference ref) {
            return FAC.newSignedInfo(
                    cm,
                    sm,
                    List.of(ref));
        }

        // Builds a KeyInfo from different sources
        private KeyInfo buildKeyInfo() throws Exception {
            KeyInfoFactory keyInfoFactory = FAC.getKeyInfoFactory();
            if (cert != null) {
                return keyInfoFactory.newKeyInfo(List.of(
                        keyInfoFactory.newX509Data(List.of(cert))));
            } else if (publicKey != null) {
                return keyInfoFactory.newKeyInfo(List.of(
                        keyInfoFactory.newKeyValue(publicKey)));
            } else if (keyName != null) {
                return keyInfoFactory.newKeyInfo(List.of(
                        keyInfoFactory.newKeyName(keyName)));
            } else {
                return null;
            }
        }
    }

    //////////// VALIDATE ////////////

    // Create a Validator, ks will be used if there is a KeyName
    public static Validator validator(KeyStore ks)
            throws Exception {
        return new Validator(ks);
    }

    // Create a Validator, key will either be inside KeyInfo or
    // a key will be provided when validate() is called
    public static Validator validator()
            throws Exception {
        return new Validator(null);
    }

    public static class Validator {

        private Boolean secureValidation = null;
        private String baseURI = null;
        private final KeyStore ks;

        public Validator(KeyStore ks) {
            this.ks = ks;
        }

        public Validator secureValidation(boolean v) {
            this.secureValidation = v;
            return this;
        }

        public Validator baseURI(String base) {
            this.baseURI = base;
            return this;
        }

        public boolean validate(Document document) throws Exception {
            return validate(document, null);
        }

        // If key is not null, any key from the signature will be ignored
        public boolean validate(Document document, PublicKey key)
                throws Exception {
            NodeList nodeList = document.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
            if (nodeList.getLength() == 1) {
                Node signatureNode = nodeList.item(0);
                if (signatureNode != null) {
                    KeySelector ks = key == null ? new MyKeySelector(this.ks) : new KeySelector() {
                        @Override
                        public KeySelectorResult select(KeyInfo ki, Purpose p,
                                AlgorithmMethod m, XMLCryptoContext c) {
                            return () -> key;
                        }
                    };
                    DOMValidateContext valContext
                            = new DOMValidateContext(ks, signatureNode);
                    if (baseURI != null) {
                        valContext.setBaseURI(baseURI);
                    }
                    if (secureValidation != null) {
                        valContext.setProperty("org.jcp.xml.dsig.secureValidation",
                                secureValidation);
                        valContext.setProperty("org.apache.jcp.xml.dsig.secureValidation",
                                secureValidation);
                    }
                    return XMLSignatureFactory.getInstance("DOM")
                            .unmarshalXMLSignature(valContext).validate(valContext);
                }
            }
            return false;
        }

        // Find public key from KeyInfo, ks will be used if it's KeyName
        private static class MyKeySelector extends KeySelector {
            private final KeyStore ks;

            public MyKeySelector(KeyStore ks) {
                this.ks = ks;
            }

            public KeySelectorResult select(KeyInfo keyInfo,
                                            KeySelector.Purpose purpose,
                                            AlgorithmMethod method,
                                            XMLCryptoContext context)
                    throws KeySelectorException {
                Objects.requireNonNull(keyInfo, "Null KeyInfo object!");

                for (XMLStructure xmlStructure : keyInfo.getContent()) {
                    PublicKey pk;
                    if (xmlStructure instanceof KeyValue kv) {
                        try {
                            pk = kv.getPublicKey();
                        } catch (KeyException ke) {
                            throw new KeySelectorException(ke);
                        }
                        return () -> pk;
                    } else if (xmlStructure instanceof X509Data x509) {
                        for (Object data : x509.getContent()) {
                            if (data instanceof X509Certificate) {
                                pk = ((X509Certificate) data).getPublicKey();
                                return () -> pk;
                            }
                        }
                    } else if (xmlStructure instanceof KeyName kn) {
                        try {
                            pk = ks.getCertificate(kn.getName()).getPublicKey();
                        } catch (KeyStoreException e) {
                            throw new KeySelectorException(e);
                        }
                        return () -> pk;
                    }
                }
                throw new KeySelectorException("No KeyValue element found!");
            }
        }
    }

    //////////// MISC ////////////

    /**
     * Adds a new rule to "jdk.xml.dsig.secureValidationPolicy"
     */

    public static void addPolicy(String rule) {
        String value = Security.getProperty("jdk.xml.dsig.secureValidationPolicy");
        value = rule + "," + value;
        Security.setProperty("jdk.xml.dsig.secureValidationPolicy", value);
    }

    private XMLUtils() {
        assert false : "No one instantiates me";
    }
}

Messung V0.5
C=92 H=91 G=91

¤ Dauer der Verarbeitung: 0.11 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.