/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
/*
 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
 */
package org.apache.xml.security.test.javax.xml.crypto.dsig;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Collections;
import java.util.Iterator;

import javax.xml.crypto.URIDereferencer;
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.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.XMLValidateContext;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
import javax.xml.crypto.dsig.keyinfo.KeyValue;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;

import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;

import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;


/**
 * This is a simple example of generating and validating a Detached XML
 * Signature using the JSR 105 API. The resulting signature will look
 * like (key and signature values will be different):
 *
 * <pre><code>
 * <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
 *   <SignedInfo>
 *     <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
 *     <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha1"/>
 *     <Reference URI="http://www.w3.org/TR/xml-stylesheet">
 *       <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
 *       <DigestValue>60NvZvtdTB+7UnlLp/H24p7h4bs=</DigestValue>
 *     </Reference>
 *   </SignedInfo>
 *   <SignatureValue>
 *     DpEylhQoiUKBoKWmYfajXO7LZxiDYgVtUtCNyTgwZgoChzorA2nhkQ==
 *   </SignatureValue>
 *   <KeyInfo>
 *     <KeyValue>
 *       <DSAKeyValue>
 *	   <P>
 *           rFto8uPQM6y34FLPmDh40BLJ1rVrC8VeRquuhPZ6jYNFkQuwxnu/wCvIAMhukPBL
 *           FET8bJf/b2ef+oqxZajEb+88zlZoyG8g/wMfDBHTxz+CnowLahnCCTYBp5kt7G8q
 *           UobJuvjylwj1st7V9Lsu03iXMXtbiriUjFa5gURasN8=
 *         </P>
 *         <Q>
 *           kEjAFpCe4lcUOdwphpzf+tBaUds=
 *         </Q>
 *         <G>
 *           oe14R2OtyKx+s+60O5BRNMOYpIg2TU/f15N3bsDErKOWtKXeNK9FS7dWStreDxo2
 *           SSgOonqAd4FuJ/4uva7GgNL4ULIqY7E+mW5iwJ7n/WTELh98mEocsLXkNh24HcH4
 *           BZfSCTruuzmCyjdV1KSqX/Eux04HfCWYmdxN3SQ/qqw=
 *         </G>
 *         <Y>
 *           pA5NnZvcd574WRXuOA7ZfC/7Lqt4cB0MRLWtHubtJoVOao9ib5ry4rTk0r6ddnOv
 *           AIGKktutzK3ymvKleS3DOrwZQgJ+/BDWDW8kO9R66o6rdjiSobBi/0c2V1+dkqOg
 *           jFmKz395mvCOZGhC7fqAVhHat2EjGPMfgSZyABa7+1k=
 *         </Y>
 * 	 </KeyValue>
 *     </DSAKeyValue>
 *   </KeyInfo>
 * </Signature>
 *
 */
public class DetachedTest {

    static {
        Security.insertProviderAt
            (new org.apache.jcp.xml.dsig.internal.dom.XMLDSigRI(), 1);
    }

    public DetachedTest() {
        //
    }

    @Test
    public void test() {
        try {
            //
            // PART 1 : Creating the detached signature
            //

            // Create a factory that will be used to generate the signature
            // structures
            XMLSignatureFactory fac = XMLSignatureFactory.getInstance
                ("DOM", new org.apache.jcp.xml.dsig.internal.dom.XMLDSigRI());

            // Create a Reference to an external URI that will be digested
            Reference ref = fac.newReference
                ("http://www.w3.org/TR/xml-stylesheet",
                fac.newDigestMethod(DigestMethod.SHA1, null));

            // Create a DSA KeyPair
            KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
            kpg.initialize(1024,
                new SecureRandom("not so random bytes".getBytes()));
            KeyPair kp = kpg.generateKeyPair();

            // Create a KeyValue containing the generated DSA PublicKey
            KeyInfoFactory kif = fac.getKeyInfoFactory();
            KeyValue kv = kif.newKeyValue(kp.getPublic());

            // Create a KeyInfo and add the KeyValue to it
            KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv));

            // Create SignedInfo
            SignedInfo si = fac.newSignedInfo(fac.newCanonicalizationMethod(
                CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS,
                (C14NMethodParameterSpec) null),
                fac.newSignatureMethod(SignatureMethod.DSA_SHA1, null),
                Collections.singletonList(ref));

            // Create XMLSignature
            XMLSignature signature = fac.newXMLSignature(si, ki, null, null, null);

            // Create an XMLSignContext and set the
            // DSA PrivateKey for signing
            Document doc = TestUtils.newDocument();
            DOMSignContext signContext = new DOMSignContext(kp.getPrivate(), doc);
            signContext.putNamespacePrefix(XMLSignature.XMLNS, "ds");

            URIDereferencer ud = new LocalHttpCacheURIDereferencer();
            signContext.setURIDereferencer(ud);

            // Generate (and sign) the XMLSignature
            signature.sign(signContext);
            TestUtils.validateSecurityOrEncryptionElement(doc.getDocumentElement());
            //
            // PART 2 : Validating the detached signature
            //

            // Create a XMLValidateContext & set the DSAPublicKey for validating
            XMLValidateContext vc = new DOMValidateContext(kp.getPublic(),
                doc.getDocumentElement());
            vc.setURIDereferencer(ud);

            // Validate the Signature (generated above)
            boolean coreValidity = signature.validate(vc);

            // Check core validation status
            if (!coreValidity) {
                // check the validation status of each Reference
                @SuppressWarnings("unchecked")
                Iterator<Reference> i = signature.getSignedInfo().getReferences().iterator();
                while (i.hasNext()) {
                    Reference reference = i.next();
                    reference.validate(vc);
                }
                fail("Signature failed core validation");
            }

            // You can also validate an XML Signature which is in XML format.
            // Unmarshal and validate an XMLSignature from a DOMValidateContext
            signature = fac.unmarshalXMLSignature(vc);
            coreValidity = signature.validate(vc);
            assertTrue(coreValidity, "Core validity of unmarshalled XMLSignature is false");
        } catch (Exception ex) {
            fail("Exception: " + ex);
        }
    }

}
