XmlSignature.java
/*
* GovWay - A customizable API Gateway
* https://govway.org
*
* Copyright (c) 2005-2024 Link.it srl (https://link.it).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3, as published by
* the Free Software Foundation.
*
* This program 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openspcoop2.utils.security;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
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.spec.C14NMethodParameterSpec;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.openspcoop2.utils.UtilsException;
import org.openspcoop2.utils.certificate.KeyStore;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* Signature
*
* @author Poli Andrea (apoli@link.it)
* @author $Author$
* @version $Rev$, $Date$
*/
public class XmlSignature {
public static final String DEFAULT_SIGNATURE_METHOD = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"; // SignatureMethod.RSA_SHA1
public static final String DEFAULT_DIGEST_METHOD = DigestMethod.SHA256;
public static final String DEFAULT_CANONICALIZATION_METHOD = CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS;
private KeyStore keystore;
private PrivateKey privateKey;
private String alias;
private String referenceUri;
private java.util.List<Transform> transforms = new ArrayList<Transform>();
private XMLSignatureFactory xmlSignatureFactory;
private javax.xml.crypto.dsig.keyinfo.KeyInfoFactory keyInfoFactory;
private KeyInfo keyInfo;
public XmlSignature(java.security.KeyStore keystore, String alias, String passwordPrivateKey) throws UtilsException{
this(new KeyStore(keystore), alias, passwordPrivateKey, false);
}
public XmlSignature(java.security.KeyStore keystore, String alias, String passwordPrivateKey, boolean addBouncyCastleProvider) throws UtilsException{
this(new KeyStore(keystore), alias, passwordPrivateKey, addBouncyCastleProvider);
}
public XmlSignature(KeyStore keystore, String alias, String passwordPrivateKey) throws UtilsException{
this(keystore, alias, passwordPrivateKey, false);
}
public XmlSignature(KeyStore keystore, String alias, String passwordPrivateKey, boolean addBouncyCastleProvider) throws UtilsException{
this.keystore = keystore;
this.privateKey = this.keystore.getPrivateKey(alias, passwordPrivateKey);
this.alias = alias;
try{
// Providers
if(addBouncyCastleProvider){
BouncyCastleProvider bouncyCastleProvider = new BouncyCastleProvider();
Security.addProvider(bouncyCastleProvider);
}
// We assemble the different parts of the Signature element into an XMLSignature object.
// These objects are all created and assembled using an XMLSignatureFactory object.
// An application obtains a DOM implementation of XMLSignatureFactory by calling the following line of code:
this.xmlSignatureFactory = XMLSignatureFactory.getInstance("DOM");
// *** default init ***
//The URI of the object to be signed (We specify a URI of "", which implies the root of the document.)
this.referenceUri = "";
// A single Transform, the enveloped Transform, which is required for enveloped signatures so that the signature itself is removed before calculating the signature value
this.addTransform(this.xmlSignatureFactory.newTransform(Transform.ENVELOPED,(TransformParameterSpec) null));
}catch(Exception e){
throw new UtilsException(e.getMessage(),e);
}
}
public String getReferenceUri() {
return this.referenceUri;
}
public void setReferenceUri(String referenceUri) {
this.referenceUri = referenceUri;
}
public java.util.List<Transform> getTransforms() {
return this.transforms;
}
public void setTransforms(java.util.List<Transform> transforms) {
this.transforms = transforms;
}
public void addTransform(Transform transform){
this.transforms.add(transform);
}
public javax.xml.crypto.dsig.keyinfo.KeyInfoFactory getKeyInfoFactory(){
if(this.keyInfoFactory==null){
this.initKeyInfoFactory();
}
return this.keyInfoFactory;
}
private synchronized void initKeyInfoFactory(){
if(this.keyInfoFactory==null){
// Next, we create the optional KeyInfo object, which contains information that enables the recipient to find the key needed to validate the signature.
// In this example, we add a KeyValue object containing the public key.
// To create KeyInfo and its various subtypes, we use a KeyInfoFactory object, which can be obtained by invoking the getKeyInfoFactory method of the XMLSignatureFactory, as follows:
this.keyInfoFactory=this.xmlSignatureFactory.getKeyInfoFactory();
}
}
public KeyInfo getKeyInfo() {
return this.keyInfo;
}
public void setKeyInfo(KeyInfo keyInfo) {
this.keyInfo = keyInfo;
}
public void addRSAKeyInfo() throws UtilsException {
this.addRSAKeyInfo(this.alias);
}
public void addRSAKeyInfo(String alias) throws UtilsException {
try{
// We then use the KeyInfoFactory to create the KeyValue object and add it to a KeyInfo object:
javax.xml.crypto.dsig.keyinfo.KeyValue keyValue = this.getKeyInfoFactory().newKeyValue(this.keystore.getCertificate(alias).getPublicKey());
this.keyInfo = this.getKeyInfoFactory().newKeyInfo(Collections.singletonList(keyValue));
}catch(Exception e){
throw new UtilsException(e.getMessage(),e);
}
}
public void addX509KeyInfo() throws UtilsException {
this.addX509KeyInfo(this.alias);
}
public void addX509KeyInfo(String alias) throws UtilsException {
try{
List<Object> x509Content = new ArrayList<>();
Certificate cert = this.keystore.getCertificate(alias);
if(cert instanceof X509Certificate){
x509Content.add(((X509Certificate)cert).getSubjectX500Principal().getName());
}
x509Content.add(cert);
javax.xml.crypto.dsig.keyinfo.X509Data x509Data = this.getKeyInfoFactory().newX509Data(x509Content);
this.keyInfo = this.getKeyInfoFactory().newKeyInfo(Collections.singletonList(x509Data));
}catch(Exception e){
throw new UtilsException(e.getMessage(),e);
}
}
public void sign(Document element) throws UtilsException{
this._sign(element.getDocumentElement(), DEFAULT_SIGNATURE_METHOD, DEFAULT_DIGEST_METHOD, DEFAULT_CANONICALIZATION_METHOD);
}
public void sign(Element element) throws UtilsException{
this._sign(element, DEFAULT_SIGNATURE_METHOD, DEFAULT_DIGEST_METHOD, DEFAULT_CANONICALIZATION_METHOD);
}
public void sign(Document element, String signatureMethod, String digestMethod, String canonicalizationMethod) throws UtilsException{
this._sign(element.getDocumentElement(), signatureMethod, digestMethod, canonicalizationMethod);
}
public void sign(Element element, String signatureMethod, String digestMethod, String canonicalizationMethod) throws UtilsException{
this._sign(element, signatureMethod, digestMethod, canonicalizationMethod);
}
public void sign(Document element, SignatureMethod signatureMethod, DigestMethod digestMethod, CanonicalizationMethod canonicalizationMethod) throws UtilsException{
this._sign(element.getDocumentElement(), signatureMethod, digestMethod, canonicalizationMethod);
}
public void sign(Element element, SignatureMethod signatureMethod, DigestMethod digestMethod, CanonicalizationMethod canonicalizationMethod) throws UtilsException{
this._sign(element, signatureMethod, digestMethod, canonicalizationMethod);
}
private void _sign(Element element, String signatureMethod, String digestMethod, String canonicalizationMethod) throws UtilsException{
try{
this._sign(element,
this.xmlSignatureFactory.newSignatureMethod(signatureMethod, null),
this.xmlSignatureFactory.newDigestMethod(digestMethod, null),
this.xmlSignatureFactory.newCanonicalizationMethod(canonicalizationMethod,(C14NMethodParameterSpec) null));
}catch(Exception e){
throw new UtilsException(e.getMessage(),e);
}
}
private void _sign(Element element, SignatureMethod signatureMethod, DigestMethod digestMethod, CanonicalizationMethod canonicalizationMethod) throws UtilsException{
try{
// We create an XML Digital Signature XMLSignContext containing input parameters for generating the signature.
// Since we are using DOM, we instantiate a DOMSignContext (a subclass of XMLSignContext), and pass it two parameters,
// the private key that will be used to sign the document and the root of the document to be signed:
DOMSignContext domSignContext = new DOMSignContext(this.privateKey, element);
// We create a Reference object che indica l'elemento da firmare
Reference signedReference = this.xmlSignatureFactory.newReference(
this.referenceUri,
digestMethod,
this.transforms, null, null);
// Next, we create the SignedInfo object, which is the object that is actually signed
SignedInfo signedInfo = this.xmlSignatureFactory.newSignedInfo(
canonicalizationMethod,
signatureMethod,
Collections.singletonList(signedReference)); // A list of References da firmare
// Finally, we create the XMLSignature object, passing as parameters the SignedInfo and KeyInfo objects that we created earlier:
XMLSignature xmlSignatureEngine = this.xmlSignatureFactory.newXMLSignature(signedInfo,this.keyInfo);
// Notice that we haven't actually generated the signature yet; we'll do that in the next step.
// Now we are ready to generate the signature, which we do by invoking the sign method on the XMLSignature object, and pass it the signing context as follows:
xmlSignatureEngine.sign(domSignContext);
}catch(Exception e){
throw new UtilsException(e.getMessage(),e);
}
}
}