cxf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cohei...@apache.org
Subject svn commit: r1562112 - /cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSecOutInterceptor.java
Date Tue, 28 Jan 2014 16:35:22 GMT
Author: coheigea
Date: Tue Jan 28 16:35:21 2014
New Revision: 1562112

URL: http://svn.apache.org/r1562112
Log:
Added an initial interceptor for streaming outbound security for JAX-RS clients

Added:
    cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSecOutInterceptor.java

Added: cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSecOutInterceptor.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSecOutInterceptor.java?rev=1562112&view=auto
==============================================================================
--- cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSecOutInterceptor.java
(added)
+++ cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSecOutInterceptor.java
Tue Jan 28 16:35:21 2014
@@ -0,0 +1,431 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.rs.security.xml;
+
+import java.io.OutputStream;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.core.Response;
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.interceptor.AbstractOutDatabindingInterceptor;
+import org.apache.cxf.interceptor.Fault;
+import org.apache.cxf.interceptor.StaxOutInterceptor;
+import org.apache.cxf.message.Exchange;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageUtils;
+import org.apache.cxf.phase.AbstractPhaseInterceptor;
+import org.apache.cxf.phase.Phase;
+import org.apache.cxf.phase.PhaseInterceptor;
+import org.apache.cxf.rs.security.common.CryptoLoader;
+import org.apache.cxf.rs.security.common.SecurityUtils;
+import org.apache.cxf.ws.security.SecurityConstants;
+import org.apache.wss4j.common.crypto.Crypto;
+import org.apache.wss4j.common.ext.WSPasswordCallback;
+import org.apache.wss4j.common.ext.WSSecurityException;
+import org.apache.xml.security.exceptions.XMLSecurityException;
+import org.apache.xml.security.signature.XMLSignature;
+import org.apache.xml.security.stax.ext.OutboundXMLSec;
+import org.apache.xml.security.stax.ext.SecurePart;
+import org.apache.xml.security.stax.ext.XMLSec;
+import org.apache.xml.security.stax.ext.XMLSecurityConstants;
+import org.apache.xml.security.stax.ext.XMLSecurityProperties;
+import org.apache.xml.security.stax.securityToken.SecurityTokenConstants;
+import org.apache.xml.security.utils.Constants;
+import org.apache.xml.security.utils.EncryptionConstants;
+import org.opensaml.xml.signature.SignatureConstants;
+
+/**
+ * A new StAX-based interceptor for creating messages with XML Signature + Encryption content.
+ */
+public class XmlSecOutInterceptor implements PhaseInterceptor<Message> {
+    public static final String OUTPUT_STREAM_HOLDER = 
+        XmlSecOutInterceptor.class.getName() + ".outputstream";
+    private static final Logger LOG = LogUtils.getL7dLogger(XmlSecOutInterceptor.class);
+    
+    private XmlSecStaxOutInterceptorInternal ending;
+    private Set<String> before = new HashSet<String>();
+    private Set<String> after = new HashSet<String>();
+    private EncryptionProperties encryptionProperties = new EncryptionProperties();
+    private SignatureProperties sigProps = new SignatureProperties();
+    private String phase;
+    //private boolean encryptSymmetricKey = true;
+    //private SecretKey symmetricKey;
+    private boolean signRequest;
+    private boolean encryptRequest;
+    private List<QName> elementsToSign = new ArrayList<QName>();
+    private List<QName> elementsToEncrypt = new ArrayList<QName>();
+
+    public XmlSecOutInterceptor() {
+        setPhase(Phase.PRE_STREAM);
+        getBefore().add(StaxOutInterceptor.class.getName());
+        
+        ending = createEndingInterceptor();
+    }
+    
+    public void handleFault(Message message) {
+    }
+
+    public void handleMessage(Message message) throws Fault {
+        OutputStream os = message.getContent(OutputStream.class);
+        String encoding = getEncoding(message);
+        
+        if (!(encryptRequest || signRequest)) {
+            Exception ex = new Exception("Either encryption and/or signature must be enabled");
+            throwFault(ex.getMessage(), ex);
+        }
+        
+        XMLStreamWriter newXMLStreamWriter = null;
+        try {
+            XMLSecurityProperties properties = new XMLSecurityProperties();
+            
+            if (encryptRequest) {
+                // Configure algorithms
+                /*
+                if (encryptionProperties != null) {
+                    if (encryptionProperties.getEncryptionKeyTransportAlgo() != null) {
+                        properties.setEncryptionKeyTransportAlgorithm(
+                            encryptionProperties.getEncryptionKeyTransportAlgo());
+                    }
+                    if (encryptionProperties.getEncryptionSymmetricKeyAlgo() != null) {
+                        properties.setEncryptionSymAlgorithm(
+                            encryptionProperties.getEncryptionSymmetricKeyAlgo());
+                    }
+                    if (encryptionProperties.getEncryptionDigestAlgo() != null) {
+                        properties.setEncryptionKeyTransportDigestAlgorithm(
+                            encryptionProperties.getEncryptionDigestAlgo());
+                    }
+                    */
+                    /* TODO
+                    if (encryptionProperties.getEncryptionKeyIdType() != null) {
+                        if (encryptionProperties.getEncryptionKeyIdType().equals(SecurityUtils.X509_CERT))
{
+                        } else if (encryptionProperties.getEncryptionKeyIdType().equals(
+                            SecurityUtils.X509_ISSUER_SERIAL)) {
+                            
+                        } else {
+                            throw new Exception("Unsupported key identifier:" + keyIdType);
+                        }
+                    }
+                }
+                */
+            }
+            
+            if (signRequest) {
+                configureSignature(message, properties);
+            }
+            
+            OutboundXMLSec outboundXMLSec = XMLSec.getOutboundXMLSec(properties);
+            
+            newXMLStreamWriter = outboundXMLSec.processOutMessage(os, encoding);
+            message.setContent(XMLStreamWriter.class, newXMLStreamWriter);
+        } catch (XMLSecurityException e) {
+            throwFault(e.getMessage(), e);
+        } catch (Exception e) {
+            throwFault(e.getMessage(), e);
+        }
+        
+        message.put(AbstractOutDatabindingInterceptor.DISABLE_OUTPUTSTREAM_OPTIMIZATION,
Boolean.TRUE);
+        message.put(StaxOutInterceptor.FORCE_START_DOCUMENT, Boolean.TRUE);
+
+        if (MessageUtils.getContextualBoolean(message, StaxOutInterceptor.FORCE_START_DOCUMENT,
false)) {
+            try {
+                newXMLStreamWriter.writeStartDocument(encoding, "1.0");
+            } catch (XMLStreamException e) {
+                throw new Fault(e);
+            }
+            message.removeContent(OutputStream.class);
+            message.put(OUTPUT_STREAM_HOLDER, os);
+        }
+
+        // Add a final interceptor to write end elements
+        message.getInterceptorChain().add(ending);
+    }
+    
+    /*
+    private void configureKeys(Message message, XMLSecurityProperties properties) 
+        throws Exception {
+        if (symmetricKey != null) {
+            properties.setEncryptionKey(symmetricKey);
+        }
+        if (encryptSymmetricKey) {
+            
+        }
+        String userName = 
+            (String)message.getContextualProperty(SecurityConstants.ENCRYPT_USERNAME);
+        CryptoLoader loader = new CryptoLoader();
+        Crypto crypto = loader.getCrypto(message, 
+                                  SecurityConstants.ENCRYPT_CRYPTO,
+                                  SecurityConstants.ENCRYPT_PROPERTIES);
+        
+        userName = SecurityUtils.getUserName(crypto, userName);
+        if (StringUtils.isEmpty(userName)) {
+            throw new Exception("User name is not available");
+        }
+        X509Certificate receiverCert = getReceiverCertificateFromCrypto(crypto, userName);
+        if (receiverCert == null) {
+            throw new Exception("Receiver certificate is not available");
+        }
+        
+        properties.setEncryptionKey(receiverCert.getPublicKey());
+        properties.setEncryptionUseThisCertificate(receiverCert);
+    }
+    
+    private X509Certificate getReceiverCertificateFromCrypto(Crypto crypto, String user)
throws Exception {
+        X509Certificate[] certs = SecurityUtils.getCertificates(crypto, user);
+        return certs[0];
+    }
+    */
+    private void configureSignature(
+        Message message, XMLSecurityProperties properties
+    ) throws Exception {
+        if (elementsToSign == null || elementsToSign.isEmpty()) {
+            throw new Exception("An Element to Sign must be specified");
+        }
+        
+        String userNameKey = SecurityConstants.SIGNATURE_USERNAME;
+        
+        CryptoLoader loader = new CryptoLoader();
+        Crypto crypto = loader.getCrypto(message, 
+                                         SecurityConstants.SIGNATURE_CRYPTO,
+                                         SecurityConstants.SIGNATURE_PROPERTIES);
+        String user = SecurityUtils.getUserName(message, crypto, userNameKey);
+         
+        if (StringUtils.isEmpty(user) || SecurityUtils.USE_REQUEST_SIGNATURE_CERT.equals(user))
{
+            throw new Exception("User name is not available");
+        }
+
+        String password = 
+            SecurityUtils.getPassword(message, user, WSPasswordCallback.SIGNATURE, this.getClass());
+    
+        X509Certificate[] issuerCerts = SecurityUtils.getCertificates(crypto, user);
+        properties.setSignatureCerts(issuerCerts);
+        
+        String sigAlgo = sigProps.getSignatureAlgo() == null 
+            ? SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1 : sigProps.getSignatureAlgo();
+        properties.setSignatureAlgorithm(sigAlgo);
+        
+        String pubKeyAlgo = issuerCerts[0].getPublicKey().getAlgorithm();
+        if (pubKeyAlgo.equalsIgnoreCase("DSA")) {
+            sigAlgo = XMLSignature.ALGO_ID_SIGNATURE_DSA;
+        }
+        PrivateKey privateKey = null;
+        try {
+            privateKey = crypto.getPrivateKey(user, password);
+        } catch (Exception ex) {
+            String errorMessage = "Private key can not be loaded, user:" + user;
+            LOG.severe(errorMessage);
+            throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, ex);
+        }
+        properties.setSignatureKey(privateKey);
+        
+        String digestAlgo = sigProps.getSignatureDigestAlgo() == null 
+            ? Constants.ALGO_ID_DIGEST_SHA1 : sigProps.getSignatureDigestAlgo();
+        properties.setSignatureDigestAlgorithm(digestAlgo);
+        
+        properties.setSignatureKeyIdentifier(
+            convertKeyIdentifier(sigProps.getSignatureKeyIdType()));
+        
+        properties.addAction(XMLSecurityConstants.SIGNATURE);
+        // Only enveloped supported for the moment.
+        SecurePart securePart = 
+            new SecurePart(elementsToSign.get(0), SecurePart.Modifier.Element,
+                           new String[]{
+                               "http://www.w3.org/2000/09/xmldsig#enveloped-signature",
+                               "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
+                           },
+                           digestAlgo);
+        properties.addSignaturePart(securePart);
+    }
+    
+    protected void throwFault(String error, Exception ex) {
+        LOG.warning(error);
+        Response response = Response.status(400).entity(error).build();
+        throw ex != null ? new BadRequestException(response, ex) : new BadRequestException(response);
+    }
+
+    public Collection<PhaseInterceptor<? extends Message>> getAdditionalInterceptors()
{
+        return null;
+    }
+
+    public Set<String> getAfter() {
+        return after;
+    }
+
+    public void setAfter(Set<String> after) {
+        this.after = after;
+    }
+
+    public Set<String> getBefore() {
+        return before;
+    }
+
+    public void setBefore(Set<String> before) {
+        this.before = before;
+    }
+
+    public String getId() {
+        return getClass().getName();
+    }
+
+    public String getPhase() {
+        return phase;
+    }
+
+    public void setPhase(String phase) {
+        this.phase = phase;
+    }
+
+    public void setEncryptionProperties(EncryptionProperties properties) {
+        this.encryptionProperties = properties;
+    }
+    
+    public void setKeyIdentifierType(String type) {
+        encryptionProperties.setEncryptionKeyIdType(type);   
+    }
+    
+    public void setSymmetricEncAlgorithm(String algo) {
+        if (!(algo.startsWith(EncryptionConstants.EncryptionSpecNS)
+            || algo.startsWith(EncryptionConstants.EncryptionSpec11NS))) {
+            algo = EncryptionConstants.EncryptionSpecNS + algo;
+        }
+        encryptionProperties.setEncryptionSymmetricKeyAlgo(algo);
+    }
+    
+    public void setKeyEncAlgorithm(String algo) {
+        encryptionProperties.setEncryptionKeyTransportAlgo(algo);
+    }
+    
+    public void setEncryptionDigestAlgorithm(String algo) {
+        encryptionProperties.setEncryptionDigestAlgo(algo);
+    }
+    
+    public void setSignatureProperties(SignatureProperties props) {
+        this.sigProps = props;
+    }
+    
+    public void setSignatureAlgorithm(String algo) {
+        sigProps.setSignatureAlgo(algo);
+    }
+    
+    public void setSignatureDigestAlgorithm(String algo) {
+        sigProps.setSignatureDigestAlgo(algo);
+    }
+    
+    public final XmlSecStaxOutInterceptorInternal createEndingInterceptor() {
+        return new XmlSecStaxOutInterceptorInternal();
+    }
+    
+    private String getEncoding(Message message) {
+        Exchange ex = message.getExchange();
+        String encoding = (String) message.get(Message.ENCODING);
+        if (encoding == null && ex.getInMessage() != null) {
+            encoding = (String) ex.getInMessage().get(Message.ENCODING);
+            message.put(Message.ENCODING, encoding);
+        }
+
+        if (encoding == null) {
+            encoding = "UTF-8";
+            message.put(Message.ENCODING, encoding);
+        }
+        return encoding;
+    }
+    
+    private static SecurityTokenConstants.KeyIdentifier convertKeyIdentifier(String keyIdentifier)
{
+        if ("IssuerSerial".equals(keyIdentifier)) {
+            return SecurityTokenConstants.KeyIdentifier_IssuerSerial;
+        } else if ("X509KeyIdentifier".equals(keyIdentifier)) {
+            return SecurityTokenConstants.KeyIdentifier_X509KeyIdentifier;
+        } else if ("SKIKeyIdentifier".equals(keyIdentifier)) {
+            return SecurityTokenConstants.KeyIdentifier_SkiKeyIdentifier;
+        } else if ("KeyValue".equals(keyIdentifier)) {
+            return SecurityTokenConstants.KeyIdentifier_KeyValue;
+        }
+        return SecurityTokenConstants.KeyIdentifier_X509KeyIdentifier;
+    }
+    
+    public boolean isSignRequest() {
+        return signRequest;
+    }
+
+    public void setSignRequest(boolean signRequest) {
+        this.signRequest = signRequest;
+    }
+
+    public boolean isEncryptRequest() {
+        return encryptRequest;
+    }
+
+    public void setEncryptRequest(boolean encryptRequest) {
+        this.encryptRequest = encryptRequest;
+    }
+
+    public void setElementsToEncrypt(List<QName> elementsToEncrypt) {
+        this.elementsToEncrypt = elementsToEncrypt;
+    }
+    
+    public void addElementToEncrypt(QName elementToEncrypt) {
+        elementsToEncrypt.add(elementToEncrypt);
+    }
+
+    public void setElementsToSign(List<QName> elementsToSign) {
+        this.elementsToSign = elementsToSign;
+    }
+    
+    public void addElementToSign(QName elementToSign) {
+        elementsToSign.add(elementToSign);
+    }
+
+    final class XmlSecStaxOutInterceptorInternal extends AbstractPhaseInterceptor<Message>
{
+        public XmlSecStaxOutInterceptorInternal() {
+            super(Phase.PRE_STREAM_ENDING);
+        }
+        
+        public void handleMessage(Message mc) throws Fault {
+            try {
+                XMLStreamWriter xtw = mc.getContent(XMLStreamWriter.class);
+                if (xtw != null) {
+                    xtw.writeEndDocument();
+                    xtw.flush();
+                    xtw.close();
+                }
+
+                OutputStream os = (OutputStream) mc.get(OUTPUT_STREAM_HOLDER);
+                if (os != null) {
+                    mc.setContent(OutputStream.class, os);
+                }
+                mc.removeContent(XMLStreamWriter.class);
+            } catch (XMLStreamException e) {
+                throw new Fault(e);
+            }
+        }
+
+    }
+}



Mime
View raw message