001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *  
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *  
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    
021    package org.apache.directory.server.kerberos.shared.crypto.encryption;
022    
023    
024    import java.io.IOException;
025    import java.util.Collections;
026    import java.util.HashMap;
027    import java.util.Map;
028    
029    import org.apache.directory.server.i18n.I18n;
030    import org.apache.directory.server.kerberos.shared.exceptions.ErrorType;
031    import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
032    import org.apache.directory.server.kerberos.shared.io.decoder.AuthenticatorDecoder;
033    import org.apache.directory.server.kerberos.shared.io.decoder.AuthorizationDataDecoder;
034    import org.apache.directory.server.kerberos.shared.io.decoder.Decoder;
035    import org.apache.directory.server.kerberos.shared.io.decoder.DecoderFactory;
036    import org.apache.directory.server.kerberos.shared.io.decoder.EncApRepPartDecoder;
037    import org.apache.directory.server.kerberos.shared.io.decoder.EncKdcRepPartDecoder;
038    import org.apache.directory.server.kerberos.shared.io.decoder.EncKrbPrivPartDecoder;
039    import org.apache.directory.server.kerberos.shared.io.decoder.EncTicketPartDecoder;
040    import org.apache.directory.server.kerberos.shared.io.decoder.EncryptedTimestampDecoder;
041    import org.apache.directory.server.kerberos.shared.io.encoder.AuthenticatorEncoder;
042    import org.apache.directory.server.kerberos.shared.io.encoder.EncApRepPartEncoder;
043    import org.apache.directory.server.kerberos.shared.io.encoder.EncAsRepPartEncoder;
044    import org.apache.directory.server.kerberos.shared.io.encoder.EncKrbPrivPartEncoder;
045    import org.apache.directory.server.kerberos.shared.io.encoder.EncTgsRepPartEncoder;
046    import org.apache.directory.server.kerberos.shared.io.encoder.EncTicketPartEncoder;
047    import org.apache.directory.server.kerberos.shared.io.encoder.Encoder;
048    import org.apache.directory.server.kerberos.shared.io.encoder.EncoderFactory;
049    import org.apache.directory.server.kerberos.shared.io.encoder.EncryptedTimestampEncoder;
050    import org.apache.directory.server.kerberos.shared.messages.AuthenticationReply;
051    import org.apache.directory.server.kerberos.shared.messages.Encodable;
052    import org.apache.directory.server.kerberos.shared.messages.TicketGrantReply;
053    import org.apache.directory.server.kerberos.shared.messages.components.Authenticator;
054    import org.apache.directory.server.kerberos.shared.messages.components.EncApRepPart;
055    import org.apache.directory.server.kerberos.shared.messages.components.EncKdcRepPart;
056    import org.apache.directory.server.kerberos.shared.messages.components.EncKrbPrivPart;
057    import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPart;
058    import org.apache.directory.server.kerberos.shared.messages.value.AuthorizationData;
059    import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
060    import org.apache.directory.server.kerberos.shared.messages.value.EncryptedTimeStamp;
061    import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
062    
063    
064    /**
065     * A Hashed Adapter encapsulating ASN.1 encoders and decoders and cipher text engines to
066     * perform seal() and unseal() operations.  A seal() operation performs an encode and an
067     * encrypt, while an unseal() operation performs a decrypt and a decode.
068     * 
069     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
070     * @version $Rev: 902575 $, $Date: 2010-01-24 15:38:06 +0100 (Sun, 24 Jan 2010) $
071     */
072    public class CipherTextHandler
073    {
074        /** a map of the default encodable class names to the encoder class names */
075        private static final Map DEFAULT_ENCODERS;
076        /** a map of the default encodable class names to the decoder class names */
077        private static final Map DEFAULT_DECODERS;
078        /** a map of the default encryption types to the encryption engine class names */
079        private static final Map DEFAULT_CIPHERS;
080    
081        static
082        {
083            Map<Class, Class> map = new HashMap<Class, Class>();
084    
085            map.put( EncryptedTimeStamp.class, EncryptedTimestampEncoder.class );
086            map.put( EncTicketPart.class, EncTicketPartEncoder.class );
087            map.put( AuthenticationReply.class, EncAsRepPartEncoder.class );
088            map.put( TicketGrantReply.class, EncTgsRepPartEncoder.class );
089            map.put( EncKrbPrivPart.class, EncKrbPrivPartEncoder.class );
090            map.put( EncApRepPart.class, EncApRepPartEncoder.class );
091            map.put( Authenticator.class, AuthenticatorEncoder.class );
092    
093            DEFAULT_ENCODERS = Collections.unmodifiableMap( map );
094        }
095    
096        static
097        {
098            Map<Class, Class> map = new HashMap<Class, Class>();
099    
100            map.put( EncTicketPart.class, EncTicketPartDecoder.class );
101            map.put( Authenticator.class, AuthenticatorDecoder.class );
102            map.put( EncryptedTimeStamp.class, EncryptedTimestampDecoder.class );
103            map.put( AuthorizationData.class, AuthorizationDataDecoder.class );
104            map.put( EncKrbPrivPart.class, EncKrbPrivPartDecoder.class );
105            map.put( EncApRepPart.class, EncApRepPartDecoder.class );
106            map.put( EncKdcRepPart.class, EncKdcRepPartDecoder.class );
107    
108            DEFAULT_DECODERS = Collections.unmodifiableMap( map );
109        }
110    
111        static
112        {
113            Map<EncryptionType, Class> map = new HashMap<EncryptionType, Class>();
114    
115            map.put( EncryptionType.DES_CBC_MD5, DesCbcMd5Encryption.class );
116            map.put( EncryptionType.DES3_CBC_SHA1_KD, Des3CbcSha1KdEncryption.class );
117            map.put( EncryptionType.AES128_CTS_HMAC_SHA1_96, Aes128CtsSha1Encryption.class );
118            map.put( EncryptionType.AES256_CTS_HMAC_SHA1_96, Aes256CtsSha1Encryption.class );
119            map.put( EncryptionType.RC4_HMAC, ArcFourHmacMd5Encryption.class );
120    
121            DEFAULT_CIPHERS = Collections.unmodifiableMap( map );
122        }
123    
124    
125        /**
126         * Performs an encode and an encrypt.
127         *
128         * @param key The key to use for encrypting.
129         * @param encodable The Kerberos object to encode.
130         * @param usage The key usage.
131         * @return The Kerberos EncryptedData.
132         * @throws KerberosException
133         */
134        public EncryptedData seal( EncryptionKey key, Encodable encodable, KeyUsage usage ) throws KerberosException
135        {
136            try
137            {
138                return encrypt( key, encode( encodable ), usage );
139            }
140            catch ( IOException ioe )
141            {
142                throw new KerberosException( ErrorType.KRB_AP_ERR_BAD_INTEGRITY, ioe );
143            }
144            catch ( ClassCastException cce )
145            {
146                throw new KerberosException( ErrorType.KRB_AP_ERR_BAD_INTEGRITY, cce );
147            }
148        }
149    
150    
151        /**
152         * Perform a decrypt and a decode.
153         *
154         * @param hint The class the encrypted data is expected to contain.
155         * @param key The key to use for decryption.
156         * @param data The data to decrypt.
157         * @param usage The key usage.
158         * @return The Kerberos object resulting from a successful decrypt and decode.
159         * @throws KerberosException
160         */
161        public Encodable unseal( Class hint, EncryptionKey key, EncryptedData data, KeyUsage usage )
162            throws KerberosException
163        {
164            try
165            {
166                return decode( hint, decrypt( key, data, usage ) );
167            }
168            catch ( IOException ioe )
169            {
170                throw new KerberosException( ErrorType.KRB_AP_ERR_BAD_INTEGRITY, ioe );
171            }
172            catch ( ClassCastException cce )
173            {
174                throw new KerberosException( ErrorType.KRB_AP_ERR_BAD_INTEGRITY, cce );
175            }
176        }
177    
178    
179        private EncryptedData encrypt( EncryptionKey key, byte[] plainText, KeyUsage usage ) throws KerberosException
180        {
181            EncryptionEngine engine = getEngine( key );
182    
183            return engine.getEncryptedData( key, plainText, usage );
184        }
185    
186    
187        private byte[] decrypt( EncryptionKey key, EncryptedData data, KeyUsage usage ) throws KerberosException
188        {
189            EncryptionEngine engine = getEngine( key );
190    
191            return engine.getDecryptedData( key, data, usage );
192        }
193    
194    
195        private byte[] encode( Encodable encodable ) throws IOException
196        {
197            Class encodableClass = encodable.getClass();
198    
199            Class clazz = ( Class ) DEFAULT_ENCODERS.get( encodableClass );
200    
201            if ( clazz == null )
202            {
203                throw new IOException( I18n.err( I18n.ERR_597, encodableClass ) );
204            }
205    
206            EncoderFactory factory = null;
207    
208            try
209            {
210                factory = ( EncoderFactory ) clazz.newInstance();
211            }
212            catch ( IllegalAccessException iae )
213            {
214                throw new IOException( I18n.err( I18n.ERR_601, encodableClass ) );
215            }
216            catch ( InstantiationException ie )
217            {
218                throw new IOException( I18n.err( I18n.ERR_599, encodableClass ) );
219            }
220    
221            Encoder encoder = factory.getEncoder();
222    
223            return encoder.encode( encodable );
224        }
225    
226    
227        private Encodable decode( Class encodable, byte[] plainText ) throws IOException
228        {
229            Class clazz = ( Class ) DEFAULT_DECODERS.get( encodable );
230    
231            if ( clazz == null )
232            {
233                throw new IOException( I18n.err( I18n.ERR_600, encodable ) );
234            }
235    
236            DecoderFactory factory = null;
237    
238            try
239            {
240                factory = ( DecoderFactory ) clazz.newInstance();
241            }
242            catch ( IllegalAccessException iae )
243            {
244                throw new IOException( I18n.err( I18n.ERR_601, encodable ) );
245            }
246            catch ( InstantiationException ie )
247            {
248                throw new IOException( I18n.err( I18n.ERR_602, encodable ) );
249            }
250    
251            Decoder decoder = factory.getDecoder();
252    
253            return decoder.decode( plainText );
254        }
255    
256    
257        private EncryptionEngine getEngine( EncryptionKey key ) throws KerberosException
258        {
259            EncryptionType encryptionType = key.getKeyType();
260    
261            Class clazz = ( Class ) DEFAULT_CIPHERS.get( encryptionType );
262    
263            if ( clazz == null )
264            {
265                throw new KerberosException( ErrorType.KDC_ERR_ETYPE_NOSUPP );
266            }
267    
268            try
269            {
270                return ( EncryptionEngine ) clazz.newInstance();
271            }
272            catch ( IllegalAccessException iae )
273            {
274                throw new KerberosException( ErrorType.KDC_ERR_ETYPE_NOSUPP, iae );
275            }
276            catch ( InstantiationException ie )
277            {
278                throw new KerberosException( ErrorType.KDC_ERR_ETYPE_NOSUPP, ie );
279            }
280        }
281    }