001/**
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 2006 Sun Microsystems Inc. All Rights Reserved
005 *
006 * The contents of this file are subject to the terms
007 * of the Common Development and Distribution License
008 * (the License). You may not use this file except in
009 * compliance with the License.
010 *
011 * You can obtain a copy of the License at
012 * https://opensso.dev.java.net/public/CDDLv1.0.html or
013 * opensso/legal/CDDLv1.0.txt
014 * See the License for the specific language governing
015 * permission and limitations under the License.
016 *
017 * When distributing Covered Code, include this CDDL
018 * Header Notice in each file and include the License file
019 * at opensso/legal/CDDLv1.0.txt.
020 * If applicable, add the following below the CDDL Header,
021 * with the fields enclosed by brackets [] replaced by
022 * your own identifying information:
023 * "Portions Copyrighted [year] [name of copyright owner]"
024 *
025 * $Id: EncryptedNameIdentifier.java,v 1.4 2008/06/25 05:46:46 qcheng Exp $
026 *
027 */
028
029package com.sun.identity.federation.message.common;
030
031import java.security.Key;
032import java.security.PrivateKey;
033import org.w3c.dom.Document;
034import org.w3c.dom.Element;
035import org.w3c.dom.Node;
036import com.sun.identity.federation.common.FSException;
037import com.sun.identity.federation.common.FSUtils;
038import com.sun.identity.federation.common.IFSConstants;
039import com.sun.identity.federation.jaxb.entityconfig.BaseConfigType;
040import com.sun.identity.federation.key.EncInfo;
041import com.sun.identity.federation.key.KeyUtil;
042import com.sun.identity.federation.meta.IDFFMetaException;
043import com.sun.identity.federation.meta.IDFFMetaManager;
044import com.sun.identity.federation.meta.IDFFMetaUtils;
045import com.sun.identity.federation.services.util.FSServiceUtils;
046import com.sun.identity.liberty.ws.meta.jaxb.ProviderDescriptorType;
047import com.sun.identity.shared.xml.XMLUtils;
048import com.sun.identity.shared.encode.Base64;
049import com.sun.identity.xmlenc.XMLEncryptionManager;
050import com.sun.identity.xmlenc.EncryptionException;
051import com.sun.identity.saml.assertion.NameIdentifier;
052import com.sun.identity.saml.common.SAMLUtils;
053import com.sun.identity.saml.common.SAMLException;
054
055/**
056 * This class <code>EncryptedNameIdentifier</code> represents a
057 * <code>EncryptableNameIdentifier</code> in an encrypted form.
058 *
059 * @supported.all.api
060 */
061public class EncryptedNameIdentifier {
062    
063    /**
064     * Returns the encryptable XML document element.
065     *
066     * @param eni the <code>EncrytableNameIdentifier</code> object.
067     *
068     * @return the <code>EncryptedNameIdentifier</code> XML Document.
069     */
070    private static Document getEncryptableDocument(
071            EncryptableNameIdentifier eni) {
072        
073        StringBuffer xml = new StringBuffer(300);
074        String NS = IFSConstants.LIB_12_NAMESPACE_STRING;
075        String appendNS = IFSConstants.LIB_PREFIX;
076        
077        xml.append("<").append(appendNS).append("EncryptedNameIdentifier")
078        .append(" ").append(NS).append(">").append(eni.toString())
079        .append("</").append(appendNS)
080        .append("EncryptedNameIdentifier").append(">");
081        if(FSUtils.debug.messageEnabled()) {
082            FSUtils.debug.message("EncryptedNameIdentifier.getEncryptable" +
083                    "NameIdentifier: doc =" + xml.toString());
084        }
085        return XMLUtils.toDOMDocument(xml.toString(), FSUtils.debug);
086        
087    }
088    
089    
090    /**
091     * Returns the <code>EncryptedNameIdentifier</code> for a given name
092     * identifier and the provider ID.
093     *
094     * @param ni the <code>NameIdentifier</code> object.
095     * @param realm The realm under which the entity resides.
096     * @param providerID the remote provider identifier.
097     * @return the <code>NameIdentifier</code> object.
098     * @throws FSException on error.
099     */
100    public static NameIdentifier getEncryptedNameIdentifier(
101            NameIdentifier ni, String realm, String providerID)
102            throws FSException {
103        
104        if(ni == null || providerID == null) {
105            FSUtils.debug.error("EncryptedNameIdentifier.construct: " +
106                    "nullInputParameter");
107            throw new FSException("nullInputParameter", null);
108        }
109        ProviderDescriptorType providerDesc = null;
110        try {
111            IDFFMetaManager metaManager = FSUtils.getIDFFMetaManager();
112            if (metaManager != null) {
113                providerDesc = metaManager.getSPDescriptor(realm, providerID);
114
115                if (providerDesc == null) {
116                    providerDesc = metaManager.getIDPDescriptor(
117                        realm, providerID);
118                }
119            }
120            if (providerDesc == null) {
121                throw new IDFFMetaException((String) null);
122            }
123        } catch (IDFFMetaException ae) {
124            FSUtils.debug.error("EncryptedNameIdentifier.construct: Could" +
125                    "not retrieve the meta for provider" + providerID);
126            throw new FSException(ae);
127        }
128
129        EncInfo encInfo = KeyUtil.getEncInfo(providerDesc, providerID, false);
130        return getEncryptedNameIdentifier(ni, providerID, 
131            encInfo.getWrappingKey(), encInfo.getDataEncAlgorithm(),
132            encInfo.getDataEncStrength());
133    }
134
135    /**
136     * Gets then Encrypted NameIdentifier for a given name identifier 
137     * and the provider ID.
138     * @param ni NameIdentifier.
139     * @param providerID Remote Provider ID.
140     * @param enckey Key Encryption Key
141     * @param dataEncAlgorithm Data encryption algorithm
142     * @param dataEncStrength Data encryption key size
143     *
144     * @return NameIdentifier EncryptedNameIdentifier. 
145     * @exception FSException for failure.
146     */
147    public static NameIdentifier getEncryptedNameIdentifier(
148        NameIdentifier ni, String providerID, Key enckey,
149        String dataEncAlgorithm, int dataEncStrength) throws FSException {
150
151        if(ni == null || providerID == null) {
152           FSUtils.debug.error("EncryptedNameIdentifier.construct: " +
153               "nullInputParameter");
154           throw new FSException("nullInputParameter", null);
155        }
156
157        EncryptableNameIdentifier eni = new EncryptableNameIdentifier(ni);
158        Document encryptableDoc = getEncryptableDocument(eni);
159        Document encryptedDoc = null;
160        
161        try {
162            Element encryptElement = (Element)encryptableDoc.
163                    getElementsByTagNameNS(IFSConstants.FF_12_XML_NS,
164                    "EncryptableNameIdentifier").item(0);
165            
166            
167            XMLEncryptionManager manager = XMLEncryptionManager.getInstance();
168            encryptedDoc = manager.encryptAndReplace(
169                    encryptableDoc,
170                    encryptElement,
171                    dataEncAlgorithm,
172                    dataEncStrength,
173                    enckey,
174                    0, // TODO: should we pick it up from extended meta?
175                    providerID);
176            
177        } catch (EncryptionException ee) {
178            FSUtils.debug.error("EncryptedNameIdentifier.construct: Unable" +
179                    "to encrypt the xml doc", ee);
180            throw new FSException(ee);
181        }
182        
183        if(encryptedDoc == null) {
184            throw new FSException("EncryptionFailed", null);
185        }
186        
187        String encodedStr = Base64.encode(
188                SAMLUtils.stringToByteArray(
189                                 XMLUtils.print((Node)(encryptedDoc))));
190        
191        try {
192            return new NameIdentifier(encodedStr, ni.getNameQualifier(),
193                    IFSConstants.NI_ENCRYPTED_FORMAT_URI);
194            
195        } catch(SAMLException se) {
196            throw new FSException(se);
197        }
198        
199    }
200    
201    /**
202     * Returns the decrypted <code>NameIdentifier</code> object.
203     *
204     * @param encNI the <code>EncryptedNameIdentifier</code> object.
205     * @param realm The realm under which the entity resides.
206     * @param providerID the Hosted Provider Identifer.
207     * @return the <code>NameIdentifier</code> object,
208     *          the decrypted <code>NameIdentifier</code>.
209     * @throws FSException on error.
210     */
211    public static NameIdentifier getDecryptedNameIdentifier(
212        NameIdentifier encNI, String realm, String providerID) 
213        throws FSException 
214    {
215        
216        if(encNI == null || providerID == null) {
217            FSUtils.debug.error("EncryptedNameIdentifier.getDecryptedName" +
218                    "Identifier: null values");
219            throw new FSException("nullInputParameter", null);
220        }
221        
222        BaseConfigType providerConfig = null;
223        try {
224            providerConfig = FSUtils.getIDFFMetaManager().
225                getSPDescriptorConfig(realm, providerID);
226            if (providerConfig == null) {
227                providerConfig = FSUtils.getIDFFMetaManager().
228                    getIDPDescriptorConfig(realm, providerID);
229            }
230        } catch (Exception ae) {
231            FSUtils.debug.error("EncryptedNameIdentifier.getDecryptedName" +
232                    "Identifier: Unable to find provider", ae);
233            throw new FSException(ae);
234        }
235        
236        if (providerConfig == null) {
237            FSUtils.debug.error("EncryptedNameIdentifier.getDecryptedName" +
238                "Identifier: Unable to find provider " + providerID);
239            throw new FSException("noProviderFound", null);
240        }
241
242        return getDecryptedNameIdentifier(encNI,
243            KeyUtil.getDecryptionKey(providerConfig));
244   }
245
246    /**
247     * Gets the decrypted NameIdentifier. 
248     * @param encNI EncryptedNameIdentifier. 
249     * @param decKey decryption key.
250     * 
251     * @return NameIdentifier Decrypted NameIdentifier.
252     * @exception FSException for failures
253     */ 
254    public static NameIdentifier getDecryptedNameIdentifier(
255        NameIdentifier encNI, PrivateKey decKey) throws FSException {
256
257
258        if(encNI.getFormat() == null ||
259                !encNI.getFormat().equals(
260                                   IFSConstants.NI_ENCRYPTED_FORMAT_URI)) {
261            throw new FSException("notValidFormat", null);
262        }
263        
264        String name = encNI.getName();
265        name = FSUtils.removeNewLineChars(name);
266        String decodeStr = SAMLUtils.byteArrayToString(Base64.decode(name));
267        
268        Document encryptedDoc =
269                XMLUtils.toDOMDocument(decodeStr, FSUtils.debug);
270        
271        try {
272            XMLEncryptionManager manager = XMLEncryptionManager.getInstance();
273            Document doc = manager.decryptAndReplace(encryptedDoc, decKey);
274            
275            Element element = (Element)doc.getElementsByTagNameNS(
276                    IFSConstants.FF_12_XML_NS,
277                    "EncryptableNameIdentifier").item(0);
278            
279            EncryptableNameIdentifier eni =
280                    new EncryptableNameIdentifier(element);
281            return new NameIdentifier(eni.getName(), eni.getNameQualifier(),
282                    eni.getFormat());
283            
284        } catch (EncryptionException ee) {
285            FSUtils.debug.error("EncryptedNameIdentifier.getDecryptedName" +
286                    "Identifier: Decryption exception", ee);
287            throw new FSException(ee);
288        } catch (SAMLException se) {
289            throw new FSException(se);
290        }
291        
292    }
293}