EncryptPartialMessageProcessor.java
- /*
- * AdroitLogic UltraESB Enterprise Service Bus
- *
- * Copyright (c) 2010-2012 AdroitLogic Private Ltd. (http://adroitlogic.org). All Rights Reserved.
- *
- * GNU Affero General Public License Usage
- *
- * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
- * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option)
- * any later version.
- *
- * 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 Affero General Public License for
- * more details.
- *
- * You should have received a copy of the GNU Affero General Public License along with this program (See LICENSE-AGPL.TXT).
- * If not, see http://www.gnu.org/licenses/agpl-3.0.html
- *
- * Commercial Usage
- *
- * Licensees holding valid UltraESB Commercial licenses may use this file in accordance with the UltraESB Commercial
- * License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written
- * agreement between you and AdroitLogic.
- *
- * If you are unsure which license is appropriate for your use, or have questions regarding the use of this file,
- * please contact AdroitLogic at info@adroitlogic.com
- */
- /*
- * Modificato da Link.it (https://link.it) per supportare le seguenti funzionalità:
- * - firma e cifratura degli attachments
- * - cifratura con chiave simmetrica
- * - supporto CRL
- *
- * Copyright (c) 2011-2025 Link.it srl (https://link.it).
- *
- */
- package org.openspcoop2.security.message.soapbox;
- import java.io.ByteArrayInputStream;
- import java.io.ByteArrayOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.security.InvalidKeyException;
- import java.security.NoSuchAlgorithmException;
- import java.security.SecureRandom;
- import java.security.Security;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import javax.activation.DataHandler;
- import javax.crypto.Cipher;
- import javax.crypto.SecretKey;
- import javax.crypto.spec.IvParameterSpec;
- import javax.xml.namespace.QName;
- import javax.xml.soap.AttachmentPart;
- import javax.xml.soap.MimeHeader;
- import javax.xml.soap.MimeHeaders;
- import javax.xml.soap.SOAPElement;
- import javax.xml.soap.SOAPException;
- import javax.xml.soap.SOAPHeaderElement;
- import org.adroitlogic.soapbox.CryptoSupport;
- import org.adroitlogic.soapbox.CryptoUtil;
- import org.adroitlogic.soapbox.EncryptionRequest;
- import org.adroitlogic.soapbox.InvalidMessageDataException;
- import org.adroitlogic.soapbox.InvalidOptionException;
- import org.adroitlogic.soapbox.MessageSecurityContext;
- import org.adroitlogic.soapbox.Processor;
- import org.adroitlogic.soapbox.SBConstants;
- import org.adroitlogic.soapbox.SecurityFailureException;
- import org.apache.xml.security.encryption.EncryptedData;
- import org.apache.xml.security.encryption.XMLCipher;
- import org.apache.xml.security.encryption.XMLEncryptionException;
- import org.apache.xml.security.keys.KeyInfo;
- import org.openspcoop2.message.OpenSPCoop2SoapMessage;
- import org.openspcoop2.security.message.constants.WSSAttachmentsConstants;
- import org.openspcoop2.utils.LoggerWrapperFactory;
- import org.openspcoop2.utils.io.Base64Utilities;
- import org.slf4j.Logger;
- import org.w3c.dom.Document;
- import org.w3c.dom.Element;
- import com.sun.xml.wss.core.EncryptedDataHeaderBlock;
- import com.sun.xml.wss.swa.MimeConstants;
- /**
- * EncryptPartialMessageProcessor
- *
- * Author of the original AdroitLogic code:
- * @author asankha
- *
- * Authors of the Link.it modification to the code:
- * @author Andrea Poli (apoli@link.it)
- * @author $Author$
- * @version $Rev$, $Date$
- */
- public class EncryptPartialMessageProcessor implements Processor {
- private static final Logger logger = LoggerWrapperFactory.getLogger(EncryptPartialMessageProcessor.class);
- private List<QName> elements;
- private List<Boolean> elementsEncryptContent;
- private Map<AttachmentPart, Boolean> attachments;
- private OpenSPCoop2SoapMessage message;
- public void setMessage(OpenSPCoop2SoapMessage message) {
- this.message = message;
- }
- private String actor;
- private boolean mustUnderstand;
- public void setActor(String actor) {
- this.actor = actor;
- }
- public void setMustUnderstand(boolean mustUnderstand) {
- this.mustUnderstand = mustUnderstand;
- }
- public EncryptPartialMessageProcessor() {
- this.elements = new ArrayList<QName>();
- this.elementsEncryptContent = new ArrayList<Boolean>();
- this.attachments = new HashMap<AttachmentPart, Boolean>();
- }
- public void addElementToEncrypt(QName element , boolean content) {
- this.elements.add(element);
- this.elementsEncryptContent.add(content);
- }
- public void addAttachmentToEncrypt(AttachmentPart part, boolean contentOnly) {
- this.attachments.put(part, contentOnly);
- }
- @Override
- public void process(org.adroitlogic.soapbox.SecurityConfig secConfig, MessageSecurityContext msgSecCtx) {
- Document doc = msgSecCtx.getDocument();
- // ensure existence of the wsse:Security header, and create one if none exists
- Element wsseSecurityElem = null;
- try{
- wsseSecurityElem = WSSUtils.getWSSecurityHeader(msgSecCtx.getDocument(), this.actor, this.mustUnderstand);
- }catch(Exception e){
- throw new SecurityFailureException(e.getMessage(), e);
- }
- //Element wsseSecurityElem = CryptoUtil.getWSSecurityHeader(doc);
-
- // we will not encrypt an already encrypted document
- if (CryptoUtil.getFirstChildOrNull(wsseSecurityElem, SBConstants.XENC, "EncryptedKey") != null) {
- throw new InvalidMessageDataException("Message is already encrypted");
- }
- Element env = doc.getDocumentElement();
- String secTokenRef = CryptoUtil.getRandomId();
- // by default Body is encrypted
- if(this.elements.isEmpty() && this.attachments.isEmpty()){
- this.elements.add(new QName(env.getNamespaceURI(), "Body"));
- this.elementsEncryptContent.add(true);
- }
- // Process element e attachments
- try {
- processElements(msgSecCtx, secTokenRef);
- processAttachments(msgSecCtx);
- } catch (Exception e) {
- throw new SecurityFailureException("Error encrypting an element or an attachment", e);
- }
-
-
-
- // Process KeyInstance
-
- Cipher cipher = null;
- String cipherValue = null;
- EncryptionRequest encReq = msgSecCtx.getEncryptionRequest();
- try {
- SoapBoxSecurityConfig secConfigOpenSPCoop = (SoapBoxSecurityConfig)secConfig;
- Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
- cipher = CryptoSupport.getInstance().getCipherInstance(encReq.getEncryptionAlgoURI());
- byte[] encKey = null;
- SecretKey encKeyObject = null;
- try {
- encKeyObject = encReq.getEphemeralKey();
- encKey = encReq.getEphemeralKey().getEncoded();
- } catch (NoSuchAlgorithmException ignore) { /*will/should not happen*/ }
- if( secConfigOpenSPCoop.isSymmetricSharedKey() ){
-
- int blockSize = cipher.getBlockSize();
- if(blockSize==0){
- blockSize = 8;
- }
- //System.out.println("cipher (Algoritmo["+cipher.getAlgorithm()+"]) blksize: " + blockSize);
-
- SecureRandom randomSecureRandom = SecureRandom.getInstance("SHA1PRNG");
- byte[] iv = new byte[blockSize];
- randomSecureRandom.nextBytes(iv);
- IvParameterSpec ivParams = new IvParameterSpec(iv);
-
- //cipher.init(Cipher.WRAP_MODE, secConfigOpenSPCoop.getSymmetricKey(encReq.getCertAlias()), new IvParameterSpec(new byte[blockSize]));
- cipher.init(Cipher.WRAP_MODE, secConfigOpenSPCoop.getSymmetricKey(encReq.getCertAlias()), ivParams);
-
- cipherValue = Base64Utilities.encodeAsString(cipher.wrap(encKeyObject));
- }
- else{
- cipher.init(Cipher.ENCRYPT_MODE, secConfig.getTrustedCertificatesByAlias(encReq.getCertAlias())[0]);
-
- if(encKey==null) {
- throw new SecurityFailureException("EncKey is null");
- }
-
- if (cipher.getBlockSize() > 0 && cipher.getBlockSize() < encKey.length) {
- throw new SecurityFailureException("Public key algorithm too weak to encrypt symmetric key " +
- " - cipher block size : " + cipher.getBlockSize() + " encrypted bytes length : " + encKey.length);
- }
- cipherValue = Base64Utilities.encodeAsString(cipher.doFinal(encKey));
- }
- } catch (InvalidKeyException e) {
- e.printStackTrace(System.err);
- throw new SecurityFailureException("Error preparing cipher for encryption : " + encReq.getEncryptionAlgoURI());
- } catch (Exception e) {
- throw new SecurityFailureException("Failed to encrypt session key", e);
- } finally {
- CryptoSupport.getInstance().returnCipherInstance(cipher);
- }
- Element encryptedKeyElem = createEncryptedKey(doc, encReq, cipherValue, secConfig, msgSecCtx, secTokenRef);
- Element firstChild = CryptoUtil.getFirstElementChild(wsseSecurityElem);
- if (firstChild != null) {
- wsseSecurityElem.insertBefore(encryptedKeyElem, firstChild);
- } else {
- wsseSecurityElem.appendChild(encryptedKeyElem);
- }
-
- }
- private Element createEncryptedKey(Document doc, EncryptionRequest encReq,
- String cipherValue, org.adroitlogic.soapbox.SecurityConfig secConfig, MessageSecurityContext msgSecCtx, String referenceId) {
- // create EncryptedKey element, and append EncryptionMethod with algorithm used
- Element encryptedKeyElem = doc.createElementNS(SBConstants.XENC, "xenc:EncryptedKey");
- encryptedKeyElem.setAttribute("Id", referenceId);
- Element encryptionMethodElem = doc.createElementNS(SBConstants.XENC, "xenc:EncryptionMethod");
- encryptionMethodElem.setAttribute("Algorithm", encReq.getEncryptionAlgoURI());
- encryptedKeyElem.appendChild(encryptionMethodElem);
- // create and attach the keyinfo element
- SoapBoxSecurityConfig securityConfigOpenSPCoop = (SoapBoxSecurityConfig) secConfig;
- if(securityConfigOpenSPCoop.isSymmetricSharedKey()){
- encryptedKeyElem.appendChild(SymmetricCryptoUtils.createKeyInfoElement(doc, encReq, msgSecCtx, secConfig));
- }else{
- //encryptedKeyElem.appendChild(CryptoUtil.createKeyInfoElement(doc, encReq, msgSecCtx, secConfig));
- encryptedKeyElem.appendChild(WSSUtils.createKeyInfoElement(doc, encReq, msgSecCtx, secConfig));
- }
- // create CipherData element and store the encrypted cipher value
- Element cipherDataElem = doc.createElementNS(SBConstants.XENC, "xenc:CipherData");
- Element cipherValueElem = doc.createElementNS(SBConstants.XENC, "xenc:CipherValue");
- cipherValueElem.setTextContent(cipherValue);
- cipherDataElem.appendChild(cipherValueElem);
- encryptedKeyElem.appendChild(cipherDataElem);
- // crate ReferenceList element and store encrypted element IDs
- Element referenceListElem = doc.createElementNS(SBConstants.XENC, "xenc:ReferenceList");
- for (String id : msgSecCtx.getEncryptedReferenceList()) {
- Element dataReferenceElem = doc.createElementNS(SBConstants.XENC, "xenc:DataReference");
- dataReferenceElem.setAttribute("URI", "#" + id);
- referenceListElem.appendChild(dataReferenceElem);
- }
- encryptedKeyElem.appendChild(referenceListElem);
- return encryptedKeyElem;
- }
- private void processElements(MessageSecurityContext msgSecCtx,
- String referenceId) throws Exception {
- Document doc = msgSecCtx.getDocument();
- Element env = doc.getDocumentElement();
- // TODO
- int index = 0;
- for(QName name : this.elements) {
- Element encElement = CryptoUtil.getFirstChild(env, name.getNamespaceURI(), name.getLocalPart());
-
- // L'attributo wsu:Id non serve nella encryption
- // String encId = encElement.getAttributeNS(SBConstants.WSU, "Id");
- // if (encId == null || encId.length() == 0) {
- // encId = encElement.getAttribute("Id");
- // }
- // if (encId == null || encId.length() == 0) {
- // encId = CryptoUtil.getRandomId();
- // encElement.setAttributeNS(SBConstants.WSU, "wsu:Id", encId);
- // CryptoUtil.setWsuId(encElement, encId);
- // }
- EncryptionRequest encReq = msgSecCtx.getEncryptionRequest();
- String symEncAlgo = encReq.getSymmetricKeyAlgoURI();
- XMLCipher xmlCipher = null;
- try {
- //xmlCipher = XMLCipher.getInstance(symEncAlgo);
- xmlCipher = CryptoSupport.getInstance().getXMLCipher(symEncAlgo);
- xmlCipher.init(XMLCipher.ENCRYPT_MODE, msgSecCtx.getEncryptionRequest().getEphemeralKey());
- EncryptedData encData = xmlCipher.getEncryptedData();
- String encEltId = CryptoUtil.getRandomId();
- encData.setId(encEltId);
- KeyInfo keyInfo = new KeyInfo(doc);
- Element securityTokenReferenceElem = doc.createElementNS(SBConstants.WSSE, "wsse:SecurityTokenReference");
- securityTokenReferenceElem.setAttributeNS(SBConstants.XMLNS, "xmlns:wsse", SBConstants.WSSE);
- Element referenceElem = doc.createElementNS(SBConstants.WSSE, "wsse:Reference");
- referenceElem.setAttribute("URI", "#" + referenceId);
- securityTokenReferenceElem.appendChild(referenceElem);
- keyInfo.addUnknownElement(securityTokenReferenceElem);
- encData.setKeyInfo(keyInfo);
- xmlCipher.doFinal(encElement.getOwnerDocument(), encElement, this.elementsEncryptContent.get(index++));
- msgSecCtx.addEncryptedReference(encEltId);
- } catch (XMLEncryptionException e) {
- throw new InvalidOptionException("Unsupported algorithm : " + symEncAlgo, e);
- } finally {
- try{
- CryptoSupport.getInstance().returnXMLCipherInstance(symEncAlgo, xmlCipher);
- }catch(Exception e){
- EncryptPartialMessageProcessor.logger.error(e.getMessage(),e);
- }
- }
- }
- }
- private static byte[] serializeHeaders(List<MimeHeader> mhs) throws Exception {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- try {
- StringBuilder line = new StringBuilder();
- for (MimeHeader mh : mhs) {
- String name = mh.getName();
- String vlue = mh.getValue();
- line.append(name);
- line.append(":");
- line.append(vlue);
- line.append("\r\n");
- }
- line.append("\r\n");
- byte[] b = line.toString().getBytes("US-ASCII");
- baos.write(b, 0, b.length);
- } catch (Exception e) {
- throw new Exception(e);
- }
- return baos.toByteArray();
- }
- private void processAttachments(MessageSecurityContext msgSecCtx) throws Exception {
- if(this.attachments.size()<=0){
- return;
- }
- Cipher _attachmentEncryptor = null;
- try {
- EncryptionRequest encReq = msgSecCtx.getEncryptionRequest();
- String encAlgo = encReq.getSymmetricKeyAlgoURI();
- _attachmentEncryptor = CryptoSupport.getInstance().getCipherInstance(encAlgo);
- _attachmentEncryptor.init(Cipher.ENCRYPT_MODE, encReq.getEphemeralKey());
- for(AttachmentPart p : this.attachments.keySet()) {
- boolean contentOnly = this.attachments.get(p);
- // create n push an ED
- EncryptedDataHeaderBlock edhb = new EncryptedDataHeaderBlock();
- String id = CryptoUtil.getRandomId();
- edhb.setId(id);
- edhb.setType( (contentOnly ? WSSAttachmentsConstants.ATTACHMENT_CONTENT_ONLY_URI : WSSAttachmentsConstants.ATTACHMENT_COMPLETE_URI));
- edhb.setMimeType(p.getContentType());
- String uri = p.getContentId();
- if (uri != null) {
- if(uri.startsWith("<")){
- uri = "cid:" + uri.substring(1, uri.length()-1);
- }else{
- uri = "cid:" + uri;
- }
- } else {
- uri = p.getContentLocation();
- }
- edhb.getCipherReference(true, uri);
- edhb.setEncryptionMethod(encAlgo);
- edhb.addTransform((contentOnly ? WSSAttachmentsConstants.ATTACHMENT_CIPHERTEXT_TRANSFORM_URI : WSSAttachmentsConstants.ATTACHMENT_COMPLETE_TRANSFORM_URI));
- //System.out.println(" --PRIMA ENCRYPT --");
- //System.out.println(org.openspcoop2.pdd.logger.Dump.dumpMessage(this.message, true));
-
- AttachmentPart encPart = EncryptPartialMessageProcessor.encryptAttachment(p, contentOnly, _attachmentEncryptor, this.message.createAttachmentPart());
- //System.out.println(" --DOPO ENCRYPT --");
- //System.out.println(org.openspcoop2.pdd.logger.Dump.dumpMessage(this.message, true));
-
- // this.attachments.remove(p); // Concurrent Modification
- MimeHeaders mhs = new MimeHeaders();
- mhs.addHeader(MimeConstants.CONTENT_ID, p.getContentId());
- this.message.removeAttachments(mhs);
- this.message.addAttachmentPart(encPart);
-
- msgSecCtx.addEncryptedReference(edhb.getId());
- //Element wssHeader = CryptoUtil.getWSSecurityHeader(msgSecCtx.getDocument());
- SOAPHeaderElement wssHeader = WSSUtils.getWSSecurityHeader(this.message, this.actor, this.mustUnderstand);
-
- SOAPElement elementToInsert = edhb.getAsSoapElement();
- wssHeader.addChildElement(elementToInsert);
-
- /*
- SOAPElement soapWssHeader = (SOAPElement) wssHeader;
- SecurityHeader _secHeader = new SecurityHeader(soapWssHeader) ;
- _secHeader.appendChild(edhb);*/
- }
- this.attachments.clear();
- }
- finally {
- CryptoSupport.getInstance().returnCipherInstance(_attachmentEncryptor);
- }
- }
- private static AttachmentPart encryptAttachment(AttachmentPart part, boolean contentOnly, Cipher cipher, AttachmentPart encPart) throws Exception {
- byte[] cipherInput = (contentOnly) ? EncryptPartialMessageProcessor.getBytesFromAttachments(part.getDataHandler()) : EncryptPartialMessageProcessor.getCipherInput(part);
- byte[] cipherOutput = cipher.doFinal(cipherInput);
-
- byte[] iv = cipher.getIV();
- byte[] encryptedBytes = new byte[iv.length + cipherOutput.length];
- System.arraycopy(iv, 0, encryptedBytes, 0, iv.length);
- System.arraycopy(cipherOutput, 0, encryptedBytes, iv.length, cipherOutput.length);
- int cLength = encryptedBytes.length;
- String cType = MimeConstants.APPLICATION_OCTET_STREAM_TYPE;
- String uri = part.getContentId();
- //Step 9 and 10.SWA spec.
- if (uri != null){
- encPart.setMimeHeader(MimeConstants.CONTENT_ID, uri);
- }else {
- uri = part.getContentLocation();
- if (uri != null){
- encPart.setMimeHeader(MimeConstants.CONTENT_LOCATION, uri);
- }
- }
- encPart.setContentType(cType);
- encPart.setMimeHeader(MimeConstants.CONTENT_LENGTH, Integer.toString(cLength));
- encPart.setMimeHeader(MimeConstants.CONTENT_TRANSFER_ENCODING, "base64");
- EncryptedAttachmentDataHandler dh = new EncryptedAttachmentDataHandler(new EncryptedAttachmentDataSource(encryptedBytes));
- encPart.setDataHandler(dh);
- cipherInput = (contentOnly) ? EncryptPartialMessageProcessor.getBytesFromAttachments(encPart.getDataHandler()) : EncryptPartialMessageProcessor.getCipherInput(encPart);
- return encPart;
- }
- private static byte[] getCipherInput(AttachmentPart part) throws Exception,
- SOAPException, IOException {
- byte[] cipherInput;
- byte[] headers = EncryptPartialMessageProcessor.getAttachmentHeaders(part.getAllMimeHeaders());
- byte[] content = EncryptPartialMessageProcessor.getBytesFromAttachments(part.getDataHandler());
- cipherInput = new byte[headers.length+content.length];
- System.arraycopy(headers, 0, cipherInput, 0, headers.length);
- System.arraycopy(content, 0, cipherInput, headers.length, content.length);
- return cipherInput;
- }
- private static byte[] getAttachmentHeaders(Iterator<MimeHeader> mhItr) throws Exception {
- List<MimeHeader> mhs = new ArrayList<MimeHeader>();
- while (mhItr.hasNext()) mhs.add(mhItr.next());
- return EncryptPartialMessageProcessor.serializeHeaders(mhs);
- }
- private static byte[] getBytesFromAttachments(DataHandler dh) throws SOAPException, IOException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- dh.writeTo(baos);
- return Base64Utilities.encodeAsString(baos.toByteArray()).getBytes("US-ASCII");
- }
- private static class EncryptedAttachmentDataHandler extends javax.activation.DataHandler {
- EncryptedAttachmentDataHandler(javax.activation.DataSource ds) {
- super(ds);
- }
- @Override
- public void writeTo(OutputStream os) throws java.io.IOException {
- ((ByteArrayOutputStream) getDataSource().getOutputStream()).writeTo(os);
- }
- }
- private static class EncryptedAttachmentDataSource implements javax.activation.DataSource {
- byte[] datasource;
- EncryptedAttachmentDataSource(byte[] ds) {
- this.datasource = ds;
- }
- @Override
- public String getContentType() {
- return MimeConstants.APPLICATION_OCTET_STREAM_TYPE;
- }
- @Override
- public InputStream getInputStream() throws java.io.IOException {
- return new ByteArrayInputStream(this.datasource);
- }
- @Override
- public String getName() {
- return "Encrypted Attachment DataSource";
- }
- @Override
- public OutputStream getOutputStream() throws java.io.IOException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- baos.write(this.datasource, 0, this.datasource.length);
- return baos;
- }
- }
- }