MessageUtilities.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.security.message.engine;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.soap.AttachmentPart;
import org.openspcoop2.message.OpenSPCoop2SoapMessage;
import org.openspcoop2.message.soap.SoapUtils;
import org.openspcoop2.message.soap.reference.AttachmentReference;
import org.openspcoop2.message.soap.reference.ElementReference;
import org.openspcoop2.message.soap.reference.Reference;
import org.openspcoop2.security.SecurityException;
import org.openspcoop2.security.message.MessageSecurityContext;
import org.openspcoop2.security.message.SubErrorCodeSecurity;
import org.openspcoop2.security.message.constants.SecurityConstants;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* MessageUtilities
*
* @author Andrea Poli (apoli@link.it)
* @author $Author$
* @version $Rev$, $Date$
*/
public class MessageUtilities {
public static void checkEncryptionPartElements(Map<QName, QName> notResolved, OpenSPCoop2SoapMessage message, List<SubErrorCodeSecurity> listaErroriInElementi) throws SecurityException {
try{
if(notResolved != null && !notResolved.isEmpty()) {
for (QName expected : notResolved.keySet()) {
QName actualQName = notResolved.get(expected);
boolean found = false;
NodeList it = message.getSOAPPart().getElementsByTagNameNS(actualQName.getNamespaceURI(), actualQName.getLocalPart());
for (int j = 0; j < it.getLength() && !found; j++) {
Node elementFather = (Node) it.item(j);
//SOAPElement elementFather = (SOAPElement) it.item(j);
//List<SOAPElement> encryptedElements = SoapUtils.getNotEmptyChildSOAPElement(elementFather);
List<Node> encryptedElements = SoapUtils.getNotEmptyChildNodes(message.getFactory(), elementFather, false);
for (int i = 0; i < encryptedElements.size() && !found; i++) {
//SOAPElement actual = encryptedElements.get(i);
Node actual = encryptedElements.get(i);
String actualNamespaceURI = actual.getNamespaceURI();
String actualLocalName = actual.getLocalName();
String expectedNamespaceURI = expected.getNamespaceURI();
String expectedLocalName = expected.getLocalPart();
if(((actualNamespaceURI == null && expectedNamespaceURI == null) || (actualNamespaceURI!=null && actualNamespaceURI.equals(expectedNamespaceURI)))
&& actualLocalName.equals(expectedLocalName)) {
//trovato l'elemento che ci interessa
found = true;
}
}
}
if(!found) {
SubErrorCodeSecurity subCodice = new SubErrorCodeSecurity();
subCodice.setEncrypt(true);
subCodice.setMsgErrore("Expected encryption part("+ expected +") not found");
subCodice.setTipo(SecurityConstants.ENCRYPTION_PART_CONTENT);
subCodice.setNamespace(expected.getNamespaceURI());
subCodice.setName(expected.getLocalPart());
listaErroriInElementi.add(subCodice);
}
}
}
}catch(Exception e){
throw new SecurityException(e.getMessage(),e);
}
}
public static Map<QName, QName> checkEncryptSignatureParts(MessageSecurityContext messageSecurityContext,List<Reference> elementsToClean, OpenSPCoop2SoapMessage message,
List<SubErrorCodeSecurity> codiciErrore, QName qnameSecurity) throws SecurityException {
Map<String, Object> properties = messageSecurityContext.getIncomingProperties();
boolean addAttachmentIdBrackets = properties.containsKey(SecurityConstants.ADD_ATTACHMENT_ID_BRACKETS) ?
properties.get(SecurityConstants.ADD_ATTACHMENT_ID_BRACKETS).equals(SecurityConstants.ADD_ATTACHMENT_ID_BRACKETS_TRUE) :
SecurityConstants.ADD_ATTACHMENT_ID_BRACKETS_DEFAULT;
try{
int numAttachmentsInMsg = message.countAttachments();
List<String> cidAttachments = new ArrayList<>();
if(numAttachmentsInMsg>0){
Iterator<?> itAttach = message.getAttachments();
while (itAttach.hasNext()) {
AttachmentPart ap = (AttachmentPart) itAttach.next();
String cid = ap.getContentId();
if (!addAttachmentIdBrackets) {
cid = cid.replaceAll("(^<)|(>$)", "");
}
cidAttachments.add(cid);
}
}
// *** ENCRYPT VERIFY ***
Map<QName, QName> notResolvedMap = new HashMap<QName, QName>();
Object encryptionPartsVerify = messageSecurityContext.getIncomingProperties().get(SecurityConstants.ENCRYPTION_PARTS_VERIFY);
Object encryptionParts = messageSecurityContext.getIncomingProperties().get(SecurityConstants.ENCRYPTION_PARTS);
if(encryptionPartsVerify!=null && "true".equalsIgnoreCase(((String)encryptionPartsVerify).trim()) &&
encryptionParts==null){
// Se forzo la proprietà verify allora devo indicare le encryption parts
throw new SecurityException(SecurityConstants.ENCRYPTION_PARTS+" non indicate");
}
if( (
encryptionPartsVerify==null ||
"true".equalsIgnoreCase(((String)encryptionPartsVerify).trim())
) &&
(encryptionParts!=null) ){
Map<String, Boolean> encryptionPartsMap = new HashMap<String, Boolean>();
Map<Reference, Boolean> encryptionReferenceMap = new HashMap<Reference, Boolean>();
boolean isAllAttachmentEncrypted = false;
int numAttachmentsEncrypted = 0;
if(encryptionParts!=null){
String[]split = ((String)encryptionParts).split(";");
int numElementsEncrypted = 0;
for (int i = 0; i < split.length; i++) {
boolean checked = false;
String[]split2 = split[i].trim().split("}");
String tipo = split2[0].trim().substring(1); // Element o Content
if("".equals(tipo)){
// caso speciale wss4j {}cid:Attachments
tipo = SecurityConstants.PART_CONTENT;
}
String namespace = null;
String nome = null;
boolean attach = false;
if(split2.length==3){
namespace = split2[1].trim().substring(1);
nome = split2[2].trim();
attach = SecurityConstants.ENCRYPTION_NAMESPACE_ATTACH.equals(namespace);
}
else{
// caso speciale wss4j {}cid:Attachments ?
if(SecurityConstants.CID_ATTACH_WSS4J.equalsIgnoreCase(split2[1].trim())){
namespace = SecurityConstants.ENCRYPTION_NAMESPACE_ATTACH;
nome = SecurityConstants.ATTACHMENT_INDEX_ALL;
attach = true;
}
else{
throw new Exception("Part ["+split[i]+"] with wrong format");
}
}
if(SecurityConstants.ENCRYPTION_PART_ELEMENT.equals(tipo)) {
// incremento solamente se non è un attachments
if(!attach){
numElementsEncrypted++;
}
}
if(nome.startsWith("{"))
nome = nome.substring(1);
//System.out.println("CIFRO ["+tipo+"] ["+namespace+"] ["+nome+"]");
List<String> cidAttachmentsEncrypt = new ArrayList<>();
if(cidAttachments!=null && cidAttachments.size()>0){
cidAttachmentsEncrypt.addAll(cidAttachments);
}
for (Reference reference : elementsToClean) {
if(reference instanceof AttachmentReference) {
if(AttachmentReference.TYPE_ENCRYPT_ATTACHMENT==reference.getType()){
if(SecurityConstants.ENCRYPTION_NAMESPACE_ATTACH.equals(namespace)){
numAttachmentsEncrypted++;
if(nome.equals(SecurityConstants.ATTACHMENT_INDEX_ALL)) {
checked = true;
isAllAttachmentEncrypted = true;
encryptionReferenceMap.put(reference, true);
encryptionPartsMap.put(split[i], checked);
// break;
// Vanno iterate tutte, dovendole contare
}else{
int position = -1;
try{
position = Integer.parseInt(nome);
}catch(Exception e){
// position non presente
}
if(position>0){
int refPosition = -1;
for (int j = 0; j < cidAttachments.size(); j++) {
if(cidAttachments.get(j).equals(reference.getReference())){
refPosition = j+1;
break;
}
}
//if(refPosition == position){ // Alcune implementazioni durante la spedizione modificano l'ordine degli attachments.
// Non è possibile effettuare tale controllo sulla ricezione. Può essere usato solo per specificare quale attach firmare/cifrare in spedizione
// verifico solamente che il cid sia presente
if(refPosition>0){
cidAttachmentsEncrypt.remove((refPosition-1));
checked = true;
encryptionReferenceMap.put(reference, true);
encryptionPartsMap.put(split[i], checked);
break;
}
}
else{
if(nome.equals(reference.getReference())) {
checked = true;
encryptionReferenceMap.put(reference, true);
encryptionPartsMap.put(split[i], checked);
break;
}
}
}
}
}
} else {
ElementReference elementReference = ((ElementReference)reference);
String localName = elementReference.getElement().getLocalName();
String namespaceURI = elementReference.getElement().getNamespaceURI();
if(ElementReference.TYPE_ENCRYPT_CONTENT==reference.getType()){
// Check encrypt content
if(nome.equals(localName) && namespace.equals(namespaceURI)) {
checked = SecurityConstants.ENCRYPTION_PART_CONTENT.equals(tipo);
encryptionReferenceMap.put(reference, checked);
encryptionPartsMap.put(split[i], checked);
if(checked){
break;
}
}
} else if(ElementReference.TYPE_ENCRYPT_ELEMENT==reference.getType() && SecurityConstants.ENCRYPTION_PART_ELEMENT.equals(tipo)) {
// Segnamo l'elemento come checked, e lo aggiungiamo alla lista di elementi da verificare
// dopo la decifratura, visto che allo stato attuale e' imposibile verificarlo
checked = true;
encryptionPartsMap.put(split[i], checked);
QName actual = new QName(namespaceURI, localName);
QName expected = new QName(namespace, nome);
boolean localPartSec = localName.equals(qnameSecurity.getLocalPart());
boolean namespaceURISec = namespaceURI.equals(qnameSecurity.getNamespaceURI());
if(!(localPartSec && namespaceURISec)) {
if(!notResolvedMap.containsKey(expected)) {
notResolvedMap.put(expected, actual);
}
}
}
}
}
}
int numElementsExpectedToBeEncrypted = 0;
for(Reference reference : elementsToClean) {
if(reference.getType()==ElementReference.TYPE_ENCRYPT_ELEMENT) {
numElementsExpectedToBeEncrypted++;
}
}
if(numElementsExpectedToBeEncrypted != numElementsEncrypted) {
SubErrorCodeSecurity subCodice = new SubErrorCodeSecurity();
subCodice.setEncrypt(true);
subCodice.setMsgErrore("Expected encryption {Element} " + numElementsExpectedToBeEncrypted + ", found " + notResolvedMap.size());
codiciErrore.add(subCodice);
}
}
if(encryptionParts!=null){
String[]split = ((String)encryptionParts).split(";");
for (int i = 0; i < split.length; i++) {
String[]split2 = split[i].trim().split("}");
String tipo = split2[0].trim().substring(1); // Element o Content
if("".equals(tipo)){
// caso speciale wss4j {}cid:Attachments
tipo = SecurityConstants.PART_CONTENT;
}
String namespace = null;
String nome = null;
if(split2.length==3){
namespace = split2[1].trim().substring(1);
nome = split2[2].trim();
}
else{
// caso speciale wss4j {}cid:Attachments ?
if(SecurityConstants.CID_ATTACH_WSS4J.equalsIgnoreCase(split2[1].trim())){
namespace = SecurityConstants.ENCRYPTION_NAMESPACE_ATTACH;
nome = SecurityConstants.ATTACHMENT_INDEX_ALL;
}
else{
throw new Exception("Part ["+split[i]+"] with wrong format");
}
}
if(nome.startsWith("{"))
nome = nome.substring(1);
if(SecurityConstants.ENCRYPTION_PART_CONTENT.equals(tipo)){
if(!encryptionPartsMap.containsKey(split[i]) || !encryptionPartsMap.get(split[i])) {
SubErrorCodeSecurity subCodice = new SubErrorCodeSecurity();
subCodice.setEncrypt(true);
subCodice.setMsgErrore("Expected encryption part("+ split[i] +") not found");
subCodice.setTipo(tipo);
subCodice.setNamespace(namespace);
subCodice.setName(nome);
codiciErrore.add(subCodice);
// throw new Exception("Expected encryption part("+ split[i] +") not found");
}
}
}
}
if(isAllAttachmentEncrypted && numAttachmentsEncrypted != numAttachmentsInMsg) {
SubErrorCodeSecurity subCodice = new SubErrorCodeSecurity();
subCodice.setEncrypt(true);
subCodice.setMsgErrore("All attachments in message (found:"+numAttachmentsInMsg+") must be encrypted, but only "+numAttachmentsEncrypted+" appear to be encrypted");
subCodice.setTipo(SecurityConstants.ENCRYPTION_PART_CONTENT);
subCodice.setNamespace(SecurityConstants.ENCRYPTION_NAMESPACE_ATTACH);
subCodice.setName(SecurityConstants.ATTACHMENT_INDEX_ALL);
codiciErrore.add(subCodice);
//throw new Exception("All attachments in message (found:"+numAttachmentsInMsg+") must be encrypted, but only "+numAttachmentsEncrypted+" appear to be encrypted");
}
for (Reference reference: elementsToClean) {
if(reference.getType()==ElementReference.TYPE_ENCRYPT_CONTENT || reference.getType()==AttachmentReference.TYPE_ENCRYPT_ATTACHMENT) {
if(!encryptionReferenceMap.containsKey(reference) || !encryptionReferenceMap.get(reference)) {
SubErrorCodeSecurity subCodice = new SubErrorCodeSecurity();
subCodice.setEncrypt(true);
subCodice.setMsgErrore("Found encryption part("+ reference+") not expected");
if(reference.getType()==ElementReference.TYPE_ENCRYPT_CONTENT){
ElementReference elReference = (ElementReference) reference;
subCodice.setTipo(SecurityConstants.ENCRYPTION_PART_CONTENT);
subCodice.setNamespace(elReference.getElement().getNamespaceURI());
subCodice.setName(elReference.getElement().getLocalName());
}
else{
subCodice.setTipo(SecurityConstants.ENCRYPTION_PART_CONTENT);
subCodice.setNamespace(SecurityConstants.ENCRYPTION_NAMESPACE_ATTACH);
subCodice.setName(reference.getReference());
}
codiciErrore.add(subCodice);
//throw new Exception("Found encryption part("+ reference+") not expected");
}
}
}
}
// *** SIGNATURE VERIFY ***
Object signaturePartsVerify = messageSecurityContext.getIncomingProperties().get(SecurityConstants.SIGNATURE_PARTS_VERIFY);
Object signatureParts = messageSecurityContext.getIncomingProperties().get(SecurityConstants.SIGNATURE_PARTS);
if(signaturePartsVerify!=null && "true".equalsIgnoreCase(((String)signaturePartsVerify).trim()) &&
signatureParts==null){
// Se forzo la proprietà verify allora devo indicare le signature parts
throw new SecurityException(SecurityConstants.SIGNATURE_PARTS+" non indicate");
}
if( (
signaturePartsVerify==null ||
"true".equalsIgnoreCase(((String)signaturePartsVerify).trim())
) &&
(signatureParts!=null) ){
boolean isAllAttachmentSigned = false;
int numAttachmentsSigned = 0;
Map<String, Boolean> signaturePartsMap = new HashMap<String, Boolean>();
Map<Reference, Boolean> referenceMap = new HashMap<Reference, Boolean>();
if(signatureParts!=null){
String[]split = ((String)signatureParts).split(";");
for (int i = 0; i < split.length; i++) {
boolean checked = false;
String[]split2 = split[i].trim().split("}");
String tipo = split2[0].trim().substring(1); // Element o Content
if("".equals(tipo)){
// caso speciale wss4j {}cid:Attachments
tipo = SecurityConstants.PART_CONTENT;
}
String namespace = null;
String nome = null;
if(split2.length==3){
namespace = split2[1].trim().substring(1);
nome = split2[2].trim();
}
else{
// caso speciale wss4j {}cid:Attachments ?
if(SecurityConstants.CID_ATTACH_WSS4J.equalsIgnoreCase(split2[1].trim())){
namespace = SecurityConstants.SIGNATURE_NAMESPACE_ATTACH;
nome = SecurityConstants.ATTACHMENT_INDEX_ALL;
}
else{
throw new Exception("Part ["+split[i]+"] with wrong format");
}
}
if(nome.startsWith("{"))
nome = nome.substring(1);
//System.out.println("CIFRO ["+tipo+"] ["+namespace+"] ["+nome+"]");
List<String> cidAttachmentsSignature = new ArrayList<>();
if(cidAttachments!=null && cidAttachments.size()>0){
cidAttachmentsSignature.addAll(cidAttachments);
}
for (Reference reference : elementsToClean) {
if(reference instanceof AttachmentReference) {
if(AttachmentReference.TYPE_SIGN_ATTACHMENT==reference.getType()){
if(SecurityConstants.SIGNATURE_NAMESPACE_ATTACH.equals(namespace)){
numAttachmentsSigned++;
if(nome.equals(SecurityConstants.ATTACHMENT_INDEX_ALL)) {
checked = true;
isAllAttachmentSigned = true;
referenceMap.put(reference, true);
// break;
// Vanno iterate tutte, dovendole contare
}
else{
int position = -1;
try{
position = Integer.parseInt(nome);
}catch(Exception e){
// position non presente
}
if(position>0){
int refPosition = -1;
for (int j = 0; j < cidAttachments.size(); j++) {
if(cidAttachments.get(j).equals(reference.getReference())){
refPosition = j+1;
break;
}
}
//if(refPosition == position){ // Alcune implementazioni durante la spedizione modificano l'ordine degli attachments.
// Non è possibile effettuare tale controllo sulla ricezione. Può essere usato solo per specificare quale attach firmare/cifrare in spedizione
// verifico solamente che il cid sia presente
if(refPosition>0){
cidAttachmentsSignature.remove((refPosition-1));
checked = true;
referenceMap.put(reference, true);
break;
}
}
else{
if(nome.equals(reference.getReference())) {
checked = true;
referenceMap.put(reference, true);
break;
}
}
}
}
}
} else {
ElementReference elementReference = ((ElementReference)reference);
String localName = elementReference.getElement().getLocalName();
String namespaceURI = elementReference.getElement().getNamespaceURI();
if(ElementReference.TYPE_SIGNATURE==reference.getType() && nome.equals(localName) && namespace.equals(namespaceURI)) {
checked = SecurityConstants.SIGNATURE_PART_COMPLETE.equals(tipo) ||
SecurityConstants.SIGNATURE_PART_CONTENT.equals(tipo) || SecurityConstants.SIGNATURE_PART_ELEMENT.equals(tipo);
referenceMap.put(reference, checked);
if(checked){
break;
}
}
}
}
signaturePartsMap.put(split[i], checked);
}
}
for (String part : signaturePartsMap.keySet()) {
if(!signaturePartsMap.get(part)) {
String[]split2 = part.trim().split("}");
String tipo = split2[0].trim().substring(1); // Element o Content
if("".equals(tipo)){
// caso speciale wss4j {}cid:Attachments
tipo = SecurityConstants.PART_CONTENT;
}
String namespace = null;
String nome = null;
if(split2.length==3){
namespace = split2[1].trim().substring(1);
nome = split2[2].trim();
}
else{
// caso speciale wss4j {}cid:Attachments ?
if(SecurityConstants.CID_ATTACH_WSS4J.equalsIgnoreCase(split2[1].trim())){
namespace = SecurityConstants.SIGNATURE_NAMESPACE_ATTACH;
nome = SecurityConstants.ATTACHMENT_INDEX_ALL;
}
else{
throw new Exception("Part ["+part+"] with wrong format");
}
}
if(nome.startsWith("{"))
nome = nome.substring(1);
SubErrorCodeSecurity subCodice = new SubErrorCodeSecurity();
subCodice.setEncrypt(false);
subCodice.setMsgErrore("Expected signature part("+ part +") not found");
subCodice.setTipo(tipo);
subCodice.setNamespace(namespace);
subCodice.setName(nome);
codiciErrore.add(subCodice);
//throw new Exception("Expected signature part("+ part +") not found");
}
}
if(isAllAttachmentSigned && numAttachmentsSigned != numAttachmentsInMsg) {
SubErrorCodeSecurity subCodice = new SubErrorCodeSecurity();
subCodice.setEncrypt(false);
subCodice.setMsgErrore("All attachments in message (found:"+numAttachmentsInMsg+") must be signed, but only "+numAttachmentsSigned+" appear to be signed");
subCodice.setTipo(SecurityConstants.SIGNATURE_PART_CONTENT);
subCodice.setNamespace(SecurityConstants.SIGNATURE_NAMESPACE_ATTACH);
subCodice.setName(SecurityConstants.ATTACHMENT_INDEX_ALL);
codiciErrore.add(subCodice);
//throw new Exception("All attachments in message (found:"+numAttachmentsInMsg+") must be signed, but "+numAttachmentsSigned+" appear to be signed");
}
for (Reference reference: elementsToClean) {
if(reference.getType()==ElementReference.TYPE_SIGNATURE || reference.getType()==AttachmentReference.TYPE_SIGN_ATTACHMENT) {
if(!referenceMap.containsKey(reference) || !referenceMap.get(reference)) {
SubErrorCodeSecurity subCodice = new SubErrorCodeSecurity();
subCodice.setEncrypt(false);
subCodice.setMsgErrore("Found signature part("+ reference+") not expected");
if(reference.getType()==ElementReference.TYPE_SIGNATURE){
ElementReference elReference = (ElementReference) reference;
subCodice.setTipo("Signature"); // Non esiste Content/Element
subCodice.setNamespace(elReference.getElement().getNamespaceURI());
subCodice.setName(elReference.getElement().getLocalName());
}
else{
subCodice.setTipo(SecurityConstants.SIGNATURE_PART_CONTENT);
subCodice.setNamespace(SecurityConstants.ENCRYPTION_NAMESPACE_ATTACH);
subCodice.setName(reference.getReference());
}
codiciErrore.add(subCodice);
//throw new Exception("Found signature part("+ reference+") not expected");
}
}
}
}
return notResolvedMap;
}catch(Exception e){
throw new SecurityException(e.getMessage(),e);
}
}
}