import javax.xml.crypto.dsig.*;
import javax.xml.crypto.dom.*;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.keyinfo.*;
import javax.xml.crypto.dsig.spec.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.security.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

public class GenEnveloped {

    public static void main(String[] args) throws Exception {

        // Instantiate the document to be signed
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        Document doc = dbf.newDocumentBuilder().parse(new FileInputStream("envelope.xml"));
        //envelope.xml is just <Envelope xmlns="urn:envelope"></Envelope>

        // Create a DOM XMLSignatureFactory that will be used to generate the
        // enveloped signature
        XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");

        // build SignatureProperties for the Timestamp element
        String timestampNS = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
        Element timestamp = doc.createElementNS(timestampNS, "ns1:Timestamp");
        String created = "2025-08-05T13:49:29+00:00";
        timestamp.appendChild(doc.createElementNS(timestampNS, "ns1:Created")).setTextContent(created);
        DOMStructure domTimestamp = new DOMStructure(timestamp);
        String signatureId = "Signature_d69345d0-6912-4027-82ee-3cf8baaa3f95";
        String sigPropId = "SignatureProperty_3205bd06-4f89-4c91-a99d-36bba026b8f3";
        SignatureProperty sigProp = fac.newSignatureProperty
                (Collections.singletonList(domTimestamp), "#"+signatureId, sigPropId);
        SignatureProperties sigProps = fac.newSignatureProperties(
                Collections.singletonList(sigProp), null);

        Reference sigprop_reference = fac.newReference
                ("#"+sigPropId,
                        fac.newDigestMethod(DigestMethod.SHA256, null),
                        Collections.singletonList
                                (fac.newTransform
                                        (CanonicalizationMethod.EXCLUSIVE,
                                                (TransformParameterSpec) null)),
                        null, null);

        List<XMLObject> xmlObjectList = new ArrayList<XMLObject>();
        xmlObjectList.add(fac.newXMLObject(Collections.singletonList(sigProps), null, null, null));

        // Create the SignedInfo
        SignedInfo si = fac.newSignedInfo
                (fac.newCanonicalizationMethod
                                (CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS,
                                        (C14NMethodParameterSpec) null),
                        fac.newSignatureMethod(SignatureMethod.RSA_SHA256, null),
                        Collections.singletonList(sigprop_reference));

        // Create a KeyPair
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
        kpg.initialize(4096);
        KeyPair kp = kpg.generateKeyPair();
        KeyInfoFactory kif = fac.getKeyInfoFactory();
        KeyValue kv = kif.newKeyValue(kp.getPublic());
        KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv));

        // Create a DOMSignContext and specify the PrivateKey and
        // location of the resulting XMLSignature's parent element
        DOMSignContext dsc = new DOMSignContext
                (kp.getPrivate(), doc.getDocumentElement());

        // Create the XMLSignature (but don't sign it yet)
        XMLSignature signature = fac.newXMLSignature(si, ki, xmlObjectList, signatureId, null);

        // Marshal, generate (and sign) the enveloped signature
        signature.sign(dsc);

        // output the resulting document
        OutputStream os = new FileOutputStream("signature.xml");

        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer trans = tf.newTransformer();
        trans.transform(new DOMSource(doc), new StreamResult(os));
    }
}