MessageSecuritySender_wss4j.java

  1. /*
  2.  * GovWay - A customizable API Gateway
  3.  * https://govway.org
  4.  *
  5.  * Copyright (c) 2005-2025 Link.it srl (https://link.it).
  6.  *
  7.  * This program is free software: you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License version 3, as published by
  9.  * the Free Software Foundation.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18.  *
  19.  */


  20. package org.openspcoop2.security.message.wss4j;


  21. import java.io.FileNotFoundException;
  22. import java.net.URISyntaxException;
  23. import java.util.ArrayList;
  24. import java.util.HashMap;
  25. import java.util.List;
  26. import java.util.Map;
  27. import java.util.Properties;

  28. import javax.security.auth.callback.CallbackHandler;
  29. import javax.xml.soap.SOAPMessage;

  30. import org.apache.cxf.binding.soap.SoapMessage;
  31. import org.apache.cxf.message.Attachment;
  32. import org.apache.cxf.message.Exchange;
  33. import org.apache.cxf.message.ExchangeImpl;
  34. import org.apache.cxf.message.MessageImpl;
  35. import org.apache.cxf.phase.PhaseInterceptor;
  36. import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;
  37. import org.apache.wss4j.common.ext.WSSecurityException;
  38. import org.apache.wss4j.dom.handler.WSHandlerConstants;
  39. import org.openspcoop2.message.MessageUtils;
  40. import org.openspcoop2.message.OpenSPCoop2Message;
  41. import org.openspcoop2.message.OpenSPCoop2SoapMessage;
  42. import org.openspcoop2.message.constants.Costanti;
  43. import org.openspcoop2.message.constants.MessageType;
  44. import org.openspcoop2.message.constants.ServiceBinding;
  45. import org.openspcoop2.protocol.sdk.state.RequestInfo;
  46. import org.openspcoop2.security.SecurityException;
  47. import org.openspcoop2.security.keystore.KeystoreConstants;
  48. import org.openspcoop2.security.message.IMessageSecuritySender;
  49. import org.openspcoop2.security.message.MessageSecurityContext;
  50. import org.openspcoop2.security.message.constants.SecurityConstants;
  51. import org.openspcoop2.security.message.saml.SAMLBuilderConfig;
  52. import org.openspcoop2.security.message.saml.SAMLCallbackHandler;
  53. import org.openspcoop2.security.message.saml.SAMLUtilities;
  54. import org.openspcoop2.security.message.utils.AttachmentProcessingPart;
  55. import org.openspcoop2.security.message.utils.AttachmentsConfigReaderUtils;
  56. import org.openspcoop2.security.message.utils.EncryptionBean;
  57. import org.openspcoop2.security.message.utils.KeystoreUtils;
  58. import org.openspcoop2.security.message.utils.SignatureBean;
  59. import org.openspcoop2.utils.Utilities;
  60. import org.openspcoop2.utils.UtilsException;
  61. import org.openspcoop2.utils.id.IDUtilities;

  62. /**
  63.  * Classe per la gestione della WS-Security (WSDoAllSender).
  64.  *
  65.  * @author Lorenzo Nardi (nardi@link.it)
  66.  * @author Tommaso Burlon (tommaso.burlon@link.it)
  67.  * @author $Author$
  68.  * @version $Rev$, $Date$
  69.  */

  70. public class MessageSecuritySender_wss4j implements IMessageSecuritySender{

  71.    
  72.      @Override
  73.     public void process(MessageSecurityContext wssContext,OpenSPCoop2Message messageParam,org.openspcoop2.utils.Map<Object> ctx) throws SecurityException{
  74.         try{    
  75.            
  76.             if(ServiceBinding.SOAP.equals(messageParam.getServiceBinding())==false){
  77.                 throw new SecurityException("WSS4J Engine usable only with SOAP Binding");
  78.             }
  79.             OpenSPCoop2SoapMessage message = messageParam.castAsSoap();
  80.            
  81.             RequestInfo requestInfo = null;
  82.             if(ctx!=null && ctx.containsKey(org.openspcoop2.core.constants.Costanti.REQUEST_INFO)) {
  83.                 requestInfo = (RequestInfo) ctx.get(org.openspcoop2.core.constants.Costanti.REQUEST_INFO);
  84.             }
  85.            
  86.            
  87.             // ** Inizializzo handler CXF **/
  88.            
  89.             WSS4JOutInterceptor ohandler = new WSS4JOutInterceptor();
  90.             PhaseInterceptor<SoapMessage> handler = ohandler.createEndingInterceptor();
  91.             SoapMessage msgCtx = new SoapMessage(new MessageImpl());
  92.             msgCtx.setVersion(MessageType.SOAP_12.equals(message.getMessageType()) ? org.apache.cxf.binding.soap.Soap12.getInstance() : org.apache.cxf.binding.soap.Soap11.getInstance());
  93.             Exchange ex = new ExchangeImpl();
  94.             ex.setInMessage(msgCtx);
  95.             SOAPMessage soapMessage = MessageUtils.getSOAPMessage(message, false, message.getTransactionId());
  96.             msgCtx.setContent(SOAPMessage.class, soapMessage);
  97.             List<?> results = new ArrayList<>();
  98.             msgCtx.put(WSHandlerConstants.RECV_RESULTS, results);
  99.            
  100.            
  101.             // ** Localizzo attachments da trattare **/
  102.            
  103.             AttachmentProcessingPart app = AttachmentsConfigReaderUtils.getSecurityOnAttachments(wssContext);
  104.            
  105.            
  106.             // ** Imposto configurazione nel messaggio **/
  107.             // NOTA: farlo dopo getSecurityOnAttachments poichè si modifica la regola di quali attachments trattare.
  108.            
  109.             setOutgoingProperties(wssContext,msgCtx,messageParam,requestInfo,ctx);
  110.            
  111.            
  112.             // ** Registro attachments da trattare **/
  113.            
  114.             List<Attachment> listAttachments = null;
  115.             if(app!=null){
  116.                 listAttachments = org.openspcoop2.security.message.wss4j.WSSUtilities.readAttachments(app, message, msgCtx);
  117.                 if(listAttachments!=null && listAttachments.size()>0){
  118.                     msgCtx.setAttachments(listAttachments);
  119.                 }
  120.             }
  121.            
  122.            
  123.             // ** Applico sicurezza tramite CXF **/
  124.            
  125.             handler.handleMessage(msgCtx);
  126.             wssContext.getLog().debug("Print wssSender results...");
  127.             org.openspcoop2.security.message.wss4j.WSSUtilities.printWSResult(wssContext.getLog(), results);
  128.            
  129.            
  130.             // ** Riporto modifica degli attachments **/
  131.            
  132.             org.openspcoop2.security.message.wss4j.WSSUtilities.updateAttachments(listAttachments, message, msgCtx);
  133.                    
  134.         }
  135.         catch(Exception e){
  136.            
  137.             String msg = Utilities.getInnerNotEmptyMessageException(e).getMessage();
  138.            
  139.             Throwable innerExc = Utilities.getLastInnerException(e);
  140.             String innerMsg = null;
  141.             if(innerExc!=null){
  142.                 innerMsg = innerExc.getMessage();
  143.             }
  144.            
  145.             String messaggio = null;
  146.             if(msg!=null){
  147.                 messaggio = new String(msg);
  148.                 if(innerMsg!=null && !innerMsg.equals(msg)){
  149.                     messaggio = messaggio + " ; " + innerMsg;
  150.                 }
  151.             }
  152.             else{
  153.                 if(innerMsg!=null){
  154.                     messaggio = innerMsg;
  155.                 }
  156.             }
  157.            
  158.             // L'if scopre l'eventuale motivo preciso riguardo al fallimento della cifratura/firma.
  159.             if(Utilities.existsInnerException(e, WSSecurityException.class)){
  160.                 Throwable t = Utilities.getLastInnerException(e);
  161.                 if(t instanceof WSSecurityException){
  162.                     if(messaggio!=null){
  163.                         messaggio = messaggio + " ; " + t.getMessage();
  164.                     }
  165.                     else{
  166.                         messaggio = t.getMessage();
  167.                     }
  168.                 }
  169.             }
  170.            
  171.             SecurityException wssException = new SecurityException(messaggio, e);
  172.             wssException.setMsgErrore(messaggio);
  173.             throw wssException;
  174.         }
  175.        
  176.     }

  177.     private void setOutgoingProperties(MessageSecurityContext wssContext,SoapMessage msgCtx,OpenSPCoop2Message message, RequestInfo requestInfo,org.openspcoop2.utils.Map<Object> ctx) throws Exception{
  178.         boolean mustUnderstand = false;
  179.         boolean signatureUser = false;
  180.         boolean user = false;
  181.         Map<String,Object> wssOutgoingProperties = wssContext.getOutgoingProperties();
  182.         if (wssOutgoingProperties != null && wssOutgoingProperties.size() > 0) {
  183.            
  184.             // preprocess per SAML
  185.             SAMLUtilities.injectSignaturePropRefIdIntoSamlConfig(wssOutgoingProperties);
  186.            
  187.             // preprocess per multipropfile
  188.             preprocessMultipropFile(wssContext, msgCtx, wssOutgoingProperties, requestInfo, ctx);
  189.            
  190.             for (String key : wssOutgoingProperties.keySet()) {
  191.                 Object oValue = wssOutgoingProperties.get(key);
  192.                 String value = null;
  193.                 if(oValue!=null && oValue instanceof String) {
  194.                     value = (String) oValue;
  195.                 }
  196. //              if (SecurityConstants.ENCRYPTION_USER.equals(key) && SecurityConstants.USE_REQ_SIG_CERT.equals(value)) {
  197. //                  // value = ...;
  198. //              }
  199.                
  200.                 // src/site/xdoc/migration/wss4j20.xml:the "samlPropFile" and "samlPropRefId" configuration tags have been removed.
  201.                 // Per ottenere lo stesso effetto di poter utilizzare tale file di proprietà, si converta la proprietà nella nuova voce: 'samlCallbackRef'
  202.                 if(SecurityConstants.SAML_PROF_FILE.equals(key)){
  203.                     //System.out.println("CONVERT ["+key+"] ["+value+"] ...");
  204.                     SAMLBuilderConfig config = SAMLBuilderConfig.getSamlConfig(value, requestInfo);
  205.                     SAMLCallbackHandler samlCallbackHandler = new SAMLCallbackHandler(config);
  206.                     msgCtx.put(SecurityConstants.SAML_CALLBACK_REF, samlCallbackHandler);
  207.                 }
  208.                 else if(SecurityConstants.SAML_PROF_REF_ID.equals(key)){
  209.                     if(oValue!=null && oValue instanceof Properties) {
  210.                         Properties p = (Properties) oValue;
  211.                         SAMLBuilderConfig config = SAMLBuilderConfig.getSamlConfig(p, requestInfo);
  212.                         SAMLCallbackHandler samlCallbackHandler = new SAMLCallbackHandler(config);
  213.                         msgCtx.put(SecurityConstants.SAML_CALLBACK_REF, samlCallbackHandler);
  214.                     }
  215.                     else {
  216.                         throw new Exception("Property ["+key+"] with uncorrect type: "+(oValue!=null ? oValue.getClass().getName() : "value is null"));
  217.                     }
  218.                 }
  219.                 else if(SecurityConstants.ENCRYPTION_PARTS.equals(key) || SecurityConstants.SIGNATURE_PARTS.equals(key)){
  220.                     if(value!=null) {
  221.                         msgCtx.put(key, normalizeWss4jParts(value,message));
  222.                     }
  223.                 }
  224.                 else if(SecurityConstants.PASSWORD_CALLBACK_REF.equals(key)) {
  225.                     msgCtx.put(key, oValue);
  226.                 }
  227.                 else if(SecurityConstants.SIGNATURE_PROPERTY_REF_ID.equals(key) ||
  228.                         SecurityConstants.SIGNATURE_VERIFICATION_PROPERTY_REF_ID.equals(key) ||
  229.                         SecurityConstants.ENCRYPTION_PROPERTY_REF_ID.equals(key) ||
  230.                         SecurityConstants.DECRYPTION_PROPERTY_REF_ID.equals(key) ) {
  231.                     if(value!=null) {
  232.                         msgCtx.put(key, value);
  233.                     }
  234.                     else {
  235.                         String id = key+"_"+IDUtilities.getUniqueSerialNumber("wssSecurity.setOutgoingProperties");
  236.                         msgCtx.put(key, id);
  237.                         msgCtx.put(id, oValue);
  238.                         if(oValue!=null && oValue instanceof Properties) {
  239.                             Properties p = (Properties) oValue;
  240.                             p.put(KeystoreConstants.PROPERTY_REQUEST_INFO, requestInfo);
  241.                         }
  242.                     }
  243.                 }
  244.                 else if(SecurityConstants.ENCRYPT_ACTION_OLD.equals(key)) {
  245.                     // backward compatibility per adeguamento costante rispetto a wss4j 2.3.x
  246.                     msgCtx.put(SecurityConstants.ENCRYPTION_ACTION, value);
  247.                 }
  248.                 else{
  249.                     msgCtx.put(key, value);
  250.                     if(SecurityConstants.MUST_UNDERSTAND.equals(key)){
  251.                         mustUnderstand = true;
  252.                     }
  253.                     else if(SecurityConstants.SIGNATURE_USER.equals(key)){
  254.                         signatureUser = true;
  255.                     }
  256.                     else if(SecurityConstants.USER.equals(key)){
  257.                         user = true;
  258.                     }
  259.                 }
  260.             }
  261.         }
  262.         if(!mustUnderstand){
  263.             //Il mustUnderstand non e' stato specificato. Lo imposto a false.
  264.             msgCtx.put(SecurityConstants.MUST_UNDERSTAND , SecurityConstants.FALSE);
  265.         }
  266.         if(wssContext.getActor()!=null){
  267.             msgCtx.put(SecurityConstants.ACTOR, wssContext.getActor());
  268.         }
  269.         if(signatureUser && !user) {
  270.             // fix: Caused by: org.apache.cxf.binding.soap.SoapFault: Empty username for specified action.
  271.             // at org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor$WSS4JOutInterceptorInternal.handleMessageInternal(WSS4JOutInterceptor.java:230) ~[cxf-rt-ws-security-3.2.6.jar:3.1.7]
  272.             //        at org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor$WSS4JOutInterceptorInternal.handleMessage(WSS4JOutInterceptor.java:135) ~[cxf-rt-ws-security-3.2.6.jar:3.1.7]
  273.             msgCtx.put(SecurityConstants.USER, (String) msgCtx.get(SecurityConstants.SIGNATURE_USER));
  274.         }
  275.     }
  276.    
  277.     private void preprocessMultipropFile(MessageSecurityContext wssContext,SoapMessage msgCtx,Map<String,Object> wssOutgoingProperties, RequestInfo requestInfo,org.openspcoop2.utils.Map<Object> ctx) throws FileNotFoundException, UtilsException, SecurityException, URISyntaxException {
  278.        
  279.         String forceSignatureUser = null;
  280.         String forceEncryptionUser = null;
  281.        
  282.         HashMap<String, String> mapAliasToPassword = new HashMap<>();
  283.         for (String key : wssOutgoingProperties.keySet()) {
  284.             if(SecurityConstants.SIGNATURE_MULTI_PROPERTY_FILE.equals(key)) {
  285.                 SignatureBean bean = KeystoreUtils.getSenderSignatureBean(wssContext, ctx);
  286.                 if(bean.getMultiKeystore()==null) {
  287.                     throw new SecurityException("Multiproperty config not exists");
  288.                 }
  289.                 String keyAlias = bean.getUser();
  290.                 String internalAlias = bean.getMultiKeystore().getInternalConfigAlias(keyAlias);
  291.                 Properties p = new Properties();
  292.                 p.put(KeystoreConstants.PROPERTY_KEYSTORE_PATH, bean.getMultiKeystore().getKeystorePath(internalAlias));
  293.                 p.put(KeystoreConstants.PROPERTY_KEYSTORE_PASSWORD, bean.getMultiKeystore().getKeystorePassword(internalAlias));
  294.                 p.put(KeystoreConstants.PROPERTY_KEYSTORE_TYPE, bean.getMultiKeystore().getKeystoreType(internalAlias));
  295.                 p.put(KeystoreConstants.PROPERTY_PROVIDER, KeystoreConstants.PROVIDER_GOVWAY);
  296.                 p.put(KeystoreConstants.PROPERTY_REQUEST_INFO, requestInfo);
  297.                 String id = SecurityConstants.SIGNATURE_PROPERTY_REF_ID+"_"+IDUtilities.getUniqueSerialNumber("wssSecurity.setOutgoingProperties");
  298.                 msgCtx.put(SecurityConstants.SIGNATURE_PROPERTY_REF_ID, id);
  299.                 msgCtx.put(id, p);
  300.                
  301.                 String password = bean.getPassword();
  302.                 msgCtx.put(SecurityConstants.SIGNATURE_PASSWORD, bean.getPassword());
  303.                 mapAliasToPassword.put(keyAlias, password);

  304.                 forceSignatureUser = keyAlias;
  305.             }
  306.             else if(SecurityConstants.ENCRYPTION_MULTI_PROPERTY_FILE.equals(key)) {
  307.                 EncryptionBean bean = KeystoreUtils.getSenderEncryptionBean(wssContext, ctx);
  308.                 if(bean.getMultiKeystore()==null) {
  309.                     throw new SecurityException("Multiproperty config not exists");
  310.                 }
  311.                 String keyAlias = bean.getUser();
  312.                 String internalAlias = bean.getMultiKeystore().getInternalConfigAlias(keyAlias);
  313.                 Properties p = new Properties();
  314.                 p.put(KeystoreConstants.PROPERTY_KEYSTORE_PATH, bean.getMultiKeystore().getKeystorePath(internalAlias));
  315.                 p.put(KeystoreConstants.PROPERTY_KEYSTORE_PASSWORD, bean.getMultiKeystore().getKeystorePassword(internalAlias));
  316.                 p.put(KeystoreConstants.PROPERTY_KEYSTORE_TYPE, bean.getMultiKeystore().getKeystoreType(internalAlias));
  317.                 p.put(KeystoreConstants.PROPERTY_PROVIDER, KeystoreConstants.PROVIDER_GOVWAY);
  318.                 p.put(KeystoreConstants.PROPERTY_REQUEST_INFO, requestInfo);
  319.                 String id = SecurityConstants.ENCRYPTION_PROPERTY_REF_ID +"_"+IDUtilities.getUniqueSerialNumber("wssSecurity.setOutgoingProperties");
  320.                 msgCtx.put(SecurityConstants.ENCRYPTION_PROPERTY_REF_ID , id);
  321.                 msgCtx.put(id, p);
  322.                
  323.                 String password = bean.getPassword();
  324.                 mapAliasToPassword.put(keyAlias, password);

  325.                 forceEncryptionUser = keyAlias;
  326.             }
  327.         }
  328.        
  329.         if (!mapAliasToPassword.isEmpty()) {
  330.             CallbackHandler pwCallbackHandler = MessageSecurityContext.newCallbackHandler(mapAliasToPassword);
  331.             msgCtx.put(SecurityConstants.PASSWORD_CALLBACK_REF, pwCallbackHandler);
  332.         }

  333.         if(forceSignatureUser!=null) {
  334.             wssOutgoingProperties.remove(SecurityConstants.SIGNATURE_USER);
  335.             wssOutgoingProperties.put(SecurityConstants.SIGNATURE_USER, forceSignatureUser);
  336.         }
  337.         if(forceEncryptionUser!=null) {
  338.             wssOutgoingProperties.remove(SecurityConstants.ENCRYPTION_USER);
  339.             wssOutgoingProperties.put(SecurityConstants.ENCRYPTION_USER, forceEncryptionUser);
  340.         }
  341.     }
  342.    
  343.     private String normalizeWss4jParts(String parts,OpenSPCoop2Message message){
  344.         StringBuilder bf = new StringBuilder();
  345.         String[]split = ((String)parts).split(";");
  346.         for (int i = 0; i < split.length; i++) {
  347.             if(i>0){
  348.                 bf.append(";");
  349.             }
  350.             String n = split[i].trim();
  351.             if(n.contains("{"+SecurityConstants.NAMESPACE_ATTACH+"}")){
  352.                 if(n.startsWith("{"+SecurityConstants.PART_ELEMENT+"}")){
  353.                     bf.append("{"+SecurityConstants.PART_ELEMENT+"}"+SecurityConstants.CID_ATTACH_WSS4J);
  354.                 }
  355.                 else {
  356.                     bf.append("{"+SecurityConstants.PART_CONTENT+"}"+SecurityConstants.CID_ATTACH_WSS4J);
  357.                 }
  358.             }
  359.             else{
  360.                 bf.append(n);
  361.             }
  362.         }
  363.         //System.out.println("PRIMA ["+parts+"] DOPO ["+bf.toString()+"]");
  364.        
  365.         String newParts = bf.toString();
  366.        
  367.         while(newParts.contains(SecurityConstants.SOAP_NAMESPACE_TEMPLATE)) {
  368.             String namespace = MessageType.SOAP_11.equals(message.getMessageType()) ? Costanti.SOAP_ENVELOPE_NAMESPACE : Costanti.SOAP12_ENVELOPE_NAMESPACE;
  369.             newParts = newParts.replace(SecurityConstants.SOAP_NAMESPACE_TEMPLATE, namespace);
  370.         }
  371.        
  372.         return newParts;
  373.     }
  374.  
  375. }