/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.ws.extensions.security;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.List;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.xml.namespace.QName;
import org.apache.xml.security.encryption.EncryptedData;
import org.apache.xml.security.encryption.XMLCipher;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.jboss.util.NotImplementedException;
import org.jboss.ws.extensions.security.element.EncryptedKey;
import org.jboss.ws.extensions.security.element.ReferenceList;
import org.jboss.ws.extensions.security.element.SecurityHeader;
import org.jboss.ws.extensions.security.element.X509Token;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public class EncryptionOperation implements EncodingOperation
{
private SecurityHeader header;
private SecurityStore store;
private static class Algorithm
{
Algorithm(String jceName, String xmlName, int size)
{
this.jceName = jceName;
this.xmlName = xmlName;
this.size = size;
}
public String jceName;
public String xmlName;
public int size;
}
private static HashMap<String, Algorithm> algorithms;
private static final String DEFAULT_ALGORITHM = "aes-128";
static
{
algorithms = new HashMap<String, Algorithm>(4);
algorithms.put("aes-128", new Algorithm("AES", XMLCipher.AES_128, 128));
algorithms.put("aes-192", new Algorithm("AES", XMLCipher.AES_192, 192));
algorithms.put("aes-256", new Algorithm("AES", XMLCipher.AES_256, 256));
algorithms.put("tripledes", new Algorithm("TripleDes", XMLCipher.TRIPLEDES, 168));
}
public EncryptionOperation(SecurityHeader header, SecurityStore store) throws WSSecurityException
{
this.header = header;
this.store = store;
}
private void processTarget(XMLCipher cipher, Document message, Target target, ReferenceList list, SecretKey key) throws WSSecurityException
{
if (!(target instanceof QNameTarget))
throw new NotImplementedException();
QName name = ((QNameTarget)target).getName();
Element element = Util.findElement(message.getDocumentElement(), name);
if (element == null)
throw new RuntimeException("Could not find element");
// Ensure that the element has an id, so that encryption verification can be performed
Util.assignWsuId(element);
try
{
cipher.init(XMLCipher.ENCRYPT_MODE, key);
EncryptedData encrypted = cipher.getEncryptedData();
String id = Util.generateId("encrypted");
encrypted.setId(id);
list.add(id);
cipher.doFinal(message, element, target.isContent());
}
catch (Exception e)
{
throw new WSSecurityException("Error encrypting target: " + name, e);
}
}
public SecretKey getSecretKey(String algorithm) throws WSSecurityException
{
Algorithm alg = algorithms.get(algorithm);
try
{
KeyGenerator kgen = KeyGenerator.getInstance(alg.jceName);
kgen.init(alg.size);
return kgen.generateKey();
}
catch (NoSuchAlgorithmException e)
{
throw new WSSecurityException(e.getMessage());
}
}
public void process(Document message, List<Target> targets, String alias, String credential, String algorithm, boolean digest, boolean useNonce, boolean useTimestamp) throws WSSecurityException
{
if (! algorithms.containsKey(algorithm))
algorithm = DEFAULT_ALGORITHM;
SecretKey secretKey = getSecretKey(algorithm);
XMLCipher cipher;
try
{
cipher = XMLCipher.getInstance(algorithms.get(algorithm).xmlName);
cipher.init(XMLCipher.ENCRYPT_MODE, secretKey);
}
catch (XMLSecurityException e)
{
throw new WSSecurityException("Error initializing xml cipher" + e.getMessage(), e);
}
ReferenceList list = new ReferenceList();
if (targets == null || targets.size() == 0)
{
// By default we encrypt the content of the body element
String namespace = message.getDocumentElement().getNamespaceURI();
processTarget(cipher, message, new QNameTarget(new QName(namespace, "Body"), true), list, secretKey);
}
else
{
for (Target target : targets)
processTarget(cipher, message, target, list, secretKey);
}
X509Certificate cert = store.getCertificate(alias);
X509Token token = (X509Token) header.getSharedToken(cert);
// Can we reuse an existing token?
if (token == null)
{
token = new X509Token(cert, message);
header.addToken(token);
}
EncryptedKey eKey = new EncryptedKey(message, secretKey, token, list);
header.addSecurityProcess(eKey);
}
}