1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.server.kerberos.protocol;
21  
22  
23  import java.util.HashSet;
24  import java.util.Map;
25  import java.util.Set;
26  
27  import javax.security.auth.kerberos.KerberosPrincipal;
28  
29  import org.apache.directory.server.kerberos.kdc.KdcServer;
30  import org.apache.directory.server.kerberos.shared.KerberosConstants;
31  import org.apache.directory.server.kerberos.shared.KerberosMessageType;
32  import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
33  import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
34  import org.apache.directory.server.kerberos.shared.crypto.encryption.KerberosKeyFactory;
35  import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
36  import org.apache.directory.server.kerberos.shared.io.encoder.EncryptedDataEncoder;
37  import org.apache.directory.server.kerberos.shared.messages.AuthenticationReply;
38  import org.apache.directory.server.kerberos.shared.messages.ErrorMessage;
39  import org.apache.directory.server.kerberos.shared.messages.KdcRequest;
40  import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
41  import org.apache.directory.server.kerberos.shared.messages.value.EncryptedTimeStamp;
42  import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
43  import org.apache.directory.server.kerberos.shared.messages.value.KdcOptions;
44  import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
45  import org.apache.directory.server.kerberos.shared.messages.value.PaData;
46  import org.apache.directory.server.kerberos.shared.messages.value.RequestBodyModifier;
47  import org.apache.directory.server.kerberos.shared.messages.value.types.PaDataType;
48  import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
49  
50  
51  /**
52   * Tests various facets of working with encryption types in the Authentication Service (AS).
53   * 
54   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
55   * @version $Rev$, $Date$
56   */
57  public class AuthenticationEncryptionTypeTest extends AbstractAuthenticationServiceTest
58  {
59      private KdcServer config;
60      private PrincipalStore store;
61      private KerberosProtocolHandler handler;
62      private DummySession session;
63  
64  
65      /**
66       * Creates a new instance of {@link AuthenticationEncryptionTypeTest}.
67       */
68      public AuthenticationEncryptionTypeTest()
69      {
70          config = new KdcServer();
71          store = new MapPrincipalStoreImpl();
72          handler = new KerberosProtocolHandler( config, store );
73          session = new DummySession();
74          lockBox = new CipherTextHandler();
75      }
76  
77  
78      /**
79       * Tests a basic request using DES-CBC-MD5.
80       * 
81       * @throws Exception
82       */
83      public void testRequestDesCbcMd5() throws Exception
84      {
85          RequestBodyModifier modifier = new RequestBodyModifier();
86          modifier.setClientName( getPrincipalName( "hnelson" ) );
87          modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
88          modifier.setRealm( "EXAMPLE.COM" );
89  
90          Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
91          encryptionTypes.add( EncryptionType.DES_CBC_MD5 );
92  
93          modifier.setEType( encryptionTypes );
94          modifier.setNonce( random.nextInt() );
95          modifier.setKdcOptions( new KdcOptions() );
96  
97          long now = System.currentTimeMillis();
98          KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
99          modifier.setTill( requestedEndTime );
100 
101         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
102         String passPhrase = "secret";
103         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
104 
105         KdcRequest message = new KdcRequest( KerberosConstants.KERBEROS_V5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
106 
107         handler.messageReceived( session, message );
108 
109         AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
110 
111         assertEquals( "Encryption type", EncryptionType.DES_CBC_MD5, reply.getEncPart().getEType() );
112     }
113 
114 
115     /**
116      * Tests the configuration of AES-128 as the sole supported encryption type.
117      * 
118      * @throws Exception
119      */
120     public void testRequestAes128() throws Exception
121     {
122         EncryptionType[] configuredEncryptionTypes =
123             { EncryptionType.AES128_CTS_HMAC_SHA1_96 };
124         config.setEncryptionTypes( configuredEncryptionTypes );
125 
126         RequestBodyModifier modifier = new RequestBodyModifier();
127         modifier.setClientName( getPrincipalName( "hnelson" ) );
128         modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
129         modifier.setRealm( "EXAMPLE.COM" );
130 
131         Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
132         encryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
133 
134         modifier.setEType( encryptionTypes );
135         modifier.setNonce( random.nextInt() );
136         modifier.setKdcOptions( new KdcOptions() );
137 
138         long now = System.currentTimeMillis();
139         KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
140         modifier.setTill( requestedEndTime );
141 
142         String principalName = "hnelson@EXAMPLE.COM";
143         String passPhrase = "secret";
144         Set<EncryptionType> preAuthEncryptionTypes = new HashSet<EncryptionType>();
145         preAuthEncryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
146 
147         Map<EncryptionType, EncryptionKey> keyMap = KerberosKeyFactory.getKerberosKeys( principalName, passPhrase,
148             preAuthEncryptionTypes );
149         EncryptionKey clientKey = keyMap.get( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
150 
151         KerberosTime timeStamp = new KerberosTime();
152         PaData[] paData = getPreAuthEncryptedTimeStamp( clientKey, timeStamp );
153 
154         KdcRequest message = new KdcRequest( KerberosConstants.KERBEROS_V5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
155 
156         handler.messageReceived( session, message );
157 
158         AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
159 
160         assertTrue( "Requested end time", requestedEndTime.equals( reply.getEndTime() ) );
161         assertTrue( "PRE_AUTHENT flag", reply.getTicket().getEncTicketPart().getFlags().isPreAuth() );
162         assertEquals( "Encryption type", EncryptionType.AES128_CTS_HMAC_SHA1_96, reply.getEncPart().getEType() );
163     }
164 
165 
166     /**
167      * Tests that the client-chosen nonce is correctly returned in the response.
168      * 
169      * @throws Exception
170      */
171     public void testNonce() throws Exception
172     {
173         EncryptionType[] configuredEncryptionTypes =
174             { EncryptionType.AES128_CTS_HMAC_SHA1_96 };
175         config.setEncryptionTypes( configuredEncryptionTypes );
176 
177         RequestBodyModifier modifier = new RequestBodyModifier();
178         modifier.setClientName( getPrincipalName( "hnelson" ) );
179         modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
180         modifier.setRealm( "EXAMPLE.COM" );
181 
182         Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
183         encryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
184 
185         modifier.setEType( encryptionTypes );
186         int nonce = random.nextInt();
187         modifier.setNonce( nonce );
188         modifier.setKdcOptions( new KdcOptions() );
189 
190         long now = System.currentTimeMillis();
191         KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
192         modifier.setTill( requestedEndTime );
193 
194         String principalName = "hnelson@EXAMPLE.COM";
195         String passPhrase = "secret";
196         Set<EncryptionType> preAuthEncryptionTypes = new HashSet<EncryptionType>();
197         preAuthEncryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
198 
199         Map<EncryptionType, EncryptionKey> keyMap = KerberosKeyFactory.getKerberosKeys( principalName, passPhrase,
200             preAuthEncryptionTypes );
201         EncryptionKey clientKey = keyMap.get( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
202 
203         KerberosTime timeStamp = new KerberosTime();
204         PaData[] paData = getPreAuthEncryptedTimeStamp( clientKey, timeStamp );
205 
206         KdcRequest message = new KdcRequest( KerberosConstants.KERBEROS_V5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
207 
208         handler.messageReceived( session, message );
209 
210         AuthenticationReply reply = ( AuthenticationReply ) session.getMessage();
211 
212         assertTrue( "Requested end time", requestedEndTime.equals( reply.getEndTime() ) );
213         assertTrue( "PRE_AUTHENT flag", reply.getTicket().getEncTicketPart().getFlags().isPreAuth() );
214         assertEquals( "Encryption type", EncryptionType.AES128_CTS_HMAC_SHA1_96, reply.getEncPart().getEType() );
215 
216         assertEquals( "Nonce", nonce, reply.getNonce() );
217     }
218 
219 
220     /**
221      * Tests when a request is made for an encryption type that is not enabled in
222      * configuration that the request fails with the correct error message.
223      * 
224      * @throws Exception
225      */
226     public void testAes128Configuration() throws Exception
227     {
228         RequestBodyModifier modifier = new RequestBodyModifier();
229         modifier.setClientName( getPrincipalName( "hnelson" ) );
230         modifier.setServerName( getPrincipalName( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ) );
231         modifier.setRealm( "EXAMPLE.COM" );
232 
233         Set<EncryptionType> requestedEncryptionTypes = new HashSet<EncryptionType>();
234         requestedEncryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
235 
236         modifier.setEType( requestedEncryptionTypes );
237         modifier.setNonce( random.nextInt() );
238         modifier.setKdcOptions( new KdcOptions() );
239 
240         long now = System.currentTimeMillis();
241         KerberosTime requestedEndTime = new KerberosTime( now + KerberosTime.DAY );
242         modifier.setTill( requestedEndTime );
243 
244         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
245         String passPhrase = "secret";
246         PaData[] paData = getPreAuthEncryptedTimeStamp( clientPrincipal, passPhrase );
247 
248         KdcRequest message = new KdcRequest( KerberosConstants.KERBEROS_V5, KerberosMessageType.AS_REQ, paData, modifier.getRequestBody() );
249 
250         handler.messageReceived( session, message );
251 
252         ErrorMessage error = ( ErrorMessage ) session.getMessage();
253         assertEquals( "KDC has no support for encryption type", 14, error.getErrorCode() );
254     }
255 
256 
257     protected PaData[] getPreAuthEncryptedTimeStamp( EncryptionKey clientKey, KerberosTime timeStamp )
258         throws Exception
259     {
260         PaData[] paData = new PaData[1];
261 
262         EncryptedTimeStamp encryptedTimeStamp = new EncryptedTimeStamp( timeStamp, 0 );
263 
264         EncryptedData encryptedData = lockBox.seal( clientKey, encryptedTimeStamp, KeyUsage.NUMBER1 );
265 
266         byte[] encodedEncryptedData = EncryptedDataEncoder.encode( encryptedData );
267 
268         PaData preAuth = new PaData();
269         preAuth.setPaDataType( PaDataType.PA_ENC_TIMESTAMP );
270         preAuth.setPaDataValue( encodedEncryptedData );
271 
272         paData[0] = preAuth;
273 
274         return paData;
275     }
276 }