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.crypto.encryption.CipherTextHandler;
31  import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
32  import org.apache.directory.server.kerberos.shared.crypto.encryption.KerberosKeyFactory;
33  import org.apache.directory.server.kerberos.shared.crypto.encryption.RandomKeyFactory;
34  import org.apache.directory.server.kerberos.shared.messages.KdcRequest;
35  import org.apache.directory.server.kerberos.shared.messages.TicketGrantReply;
36  import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPartModifier;
37  import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
38  import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
39  import org.apache.directory.server.kerberos.shared.messages.value.KdcOptions;
40  import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
41  import org.apache.directory.server.kerberos.shared.messages.value.RequestBody;
42  import org.apache.directory.server.kerberos.shared.messages.value.RequestBodyModifier;
43  import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
44  
45  
46  /**
47   * Tests various facets of working with encryption types in the Ticket-Granting Service (TGS).
48   *
49   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
50   * @version $Rev$, $Date$
51   */
52  public class TicketGrantingEncryptionTypeTest extends AbstractTicketGrantingServiceTest
53  {
54      private KdcServer config;
55      private PrincipalStore store;
56      private KerberosProtocolHandler handler;
57      private DummySession session;
58  
59  
60      /**
61       * Creates a new instance of {@link TicketGrantingEncryptionTypeTest}.
62       */
63      public TicketGrantingEncryptionTypeTest()
64      {
65          config = new KdcServer();
66  
67          /*
68           * Body checksum verification must be disabled because we are bypassing
69           * the codecs, where the body bytes are set on the KdcRequest message.
70           */
71          config.setBodyChecksumVerified( false );
72  
73          store = new MapPrincipalStoreImpl();
74          handler = new KerberosProtocolHandler( config, store );
75          session = new DummySession();
76          lockBox = new CipherTextHandler();
77      }
78  
79  
80      /**
81       * Tests a basic request using DES-CBC-MD5.
82       *
83       * @throws Exception
84       */
85      public void testRequestDesCbcMd5() throws Exception
86      {
87          // Get the mutable ticket part.
88          KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
89          EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
90  
91          // Seal the ticket for the server.
92          KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
93          String passPhrase = "randomKey";
94          EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
95          Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
96  
97          RequestBodyModifier modifier = new RequestBodyModifier();
98          modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
99          modifier.setRealm( "EXAMPLE.COM" );
100 
101         Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
102         encryptionTypes.add( EncryptionType.DES_CBC_MD5 );
103 
104         modifier.setEType( encryptionTypes );
105 
106         modifier.setNonce( random.nextInt() );
107 
108         KdcOptions kdcOptions = new KdcOptions();
109         modifier.setKdcOptions( kdcOptions );
110 
111         long now = System.currentTimeMillis();
112         KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
113         modifier.setTill( requestedEndTime );
114 
115         RequestBody requestBody = modifier.getRequestBody();
116         KdcRequest message = getKdcRequest( tgt, requestBody );
117 
118         handler.messageReceived( session, message );
119 
120         TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
121 
122         assertEquals( "Encryption type", EncryptionType.DES_CBC_MD5, reply.getEncPart().getEType() );
123     }
124 
125 
126     /**
127      * Tests the use of a TGT containing a DES-CBC-MD5 session key while the
128      * requested encryption type is AES-128.
129      *
130      * @throws Exception
131      */
132     public void testRequestAes128() throws Exception
133     {
134         EncryptionType[] configuredEncryptionTypes =
135                 {EncryptionType.AES128_CTS_HMAC_SHA1_96};
136         config.setEncryptionTypes( configuredEncryptionTypes );
137 
138         // Get the mutable ticket part.
139         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
140         EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
141 
142         // Seal the ticket for the server.
143         KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
144         String passPhrase = "randomKey";
145         EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
146         Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
147 
148         RequestBodyModifier modifier = new RequestBodyModifier();
149         modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
150         modifier.setRealm( "EXAMPLE.COM" );
151 
152         Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
153         encryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
154 
155         modifier.setEType( encryptionTypes );
156 
157         modifier.setNonce( random.nextInt() );
158 
159         KdcOptions kdcOptions = new KdcOptions();
160         modifier.setKdcOptions( kdcOptions );
161 
162         long now = System.currentTimeMillis();
163         KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
164         modifier.setTill( requestedEndTime );
165 
166         RequestBody requestBody = modifier.getRequestBody();
167         KdcRequest message = getKdcRequest( tgt, requestBody );
168 
169         handler.messageReceived( session, message );
170 
171         TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
172 
173         assertEquals( "Encryption type", EncryptionType.DES_CBC_MD5, reply.getEncPart().getEType() );
174         assertEquals( "Encryption type", EncryptionType.AES128_CTS_HMAC_SHA1_96, reply.getTicket().getEncPart()
175                 .getEType() );
176     }
177 
178 
179     /**
180      * Tests the use of a TGT containing an AES-128 session key while the
181      * requested encryption type is also AES-128.
182      *
183      * @throws Exception
184      */
185     public void testRequestAes128TgtAndRequested() throws Exception
186     {
187         EncryptionType[] configuredEncryptionTypes =
188                 {EncryptionType.AES128_CTS_HMAC_SHA1_96};
189         config.setEncryptionTypes( configuredEncryptionTypes );
190 
191         // Get the mutable ticket part.
192         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
193         EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
194 
195         // Make changes to test.
196         sessionKey = RandomKeyFactory.getRandomKey( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
197         encTicketPartModifier.setSessionKey( sessionKey );
198 
199         // Seal the ticket for the server.
200         String principalName = "krbtgt/EXAMPLE.COM@EXAMPLE.COM";
201         KerberosPrincipal serverPrincipal = new KerberosPrincipal( principalName );
202         String passPhrase = "randomKey";
203         Set<EncryptionType> preAuthEncryptionTypes = new HashSet<EncryptionType>();
204         preAuthEncryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
205 
206         Map<EncryptionType, EncryptionKey> keyMap = KerberosKeyFactory.getKerberosKeys( principalName, passPhrase,
207                 preAuthEncryptionTypes );
208         EncryptionKey serverKey = keyMap.get( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
209 
210         Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
211 
212         RequestBodyModifier modifier = new RequestBodyModifier();
213         modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
214         modifier.setRealm( "EXAMPLE.COM" );
215 
216         Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
217         encryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
218 
219         modifier.setEType( encryptionTypes );
220 
221         modifier.setNonce( random.nextInt() );
222 
223         KdcOptions kdcOptions = new KdcOptions();
224         modifier.setKdcOptions( kdcOptions );
225 
226         long now = System.currentTimeMillis();
227         KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
228         modifier.setTill( requestedEndTime );
229 
230         RequestBody requestBody = modifier.getRequestBody();
231         KdcRequest message = getKdcRequest( tgt, requestBody );
232 
233         handler.messageReceived( session, message );
234 
235         TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
236 
237         assertEquals( "Encryption type", EncryptionType.AES128_CTS_HMAC_SHA1_96, reply.getEncPart().getEType() );
238         assertEquals( "Encryption type", EncryptionType.AES128_CTS_HMAC_SHA1_96, reply.getTicket().getEncPart()
239                 .getEType() );
240     }
241 
242 
243     /**
244      * Tests that the client-chosen nonce is correctly returned in the response.
245      *
246      * @throws Exception
247      */
248     public void testNonce() throws Exception
249     {
250         EncryptionType[] configuredEncryptionTypes =
251                 {EncryptionType.AES128_CTS_HMAC_SHA1_96};
252         config.setEncryptionTypes( configuredEncryptionTypes );
253 
254         // Get the mutable ticket part.
255         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
256         EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
257 
258         // Make changes to test.
259         sessionKey = RandomKeyFactory.getRandomKey( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
260         encTicketPartModifier.setSessionKey( sessionKey );
261 
262         // Seal the ticket for the server.
263         String principalName = "krbtgt/EXAMPLE.COM@EXAMPLE.COM";
264         KerberosPrincipal serverPrincipal = new KerberosPrincipal( principalName );
265         String passPhrase = "randomKey";
266         Set<EncryptionType> preAuthEncryptionTypes = new HashSet<EncryptionType>();
267         preAuthEncryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
268 
269         Map<EncryptionType, EncryptionKey> keyMap = KerberosKeyFactory.getKerberosKeys( principalName, passPhrase,
270                 preAuthEncryptionTypes );
271         EncryptionKey serverKey = keyMap.get( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
272 
273         Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
274 
275         RequestBodyModifier modifier = new RequestBodyModifier();
276         modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
277         modifier.setRealm( "EXAMPLE.COM" );
278 
279         Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
280         encryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
281 
282         modifier.setEType( encryptionTypes );
283 
284         int nonce = random.nextInt();
285         modifier.setNonce( nonce );
286 
287         KdcOptions kdcOptions = new KdcOptions();
288         modifier.setKdcOptions( kdcOptions );
289 
290         long now = System.currentTimeMillis();
291         KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
292         modifier.setTill( requestedEndTime );
293 
294         RequestBody requestBody = modifier.getRequestBody();
295         KdcRequest message = getKdcRequest( tgt, requestBody );
296 
297         handler.messageReceived( session, message );
298 
299         TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
300 
301         assertEquals( "Encryption type", EncryptionType.AES128_CTS_HMAC_SHA1_96, reply.getEncPart().getEType() );
302         assertEquals( "Encryption type", EncryptionType.AES128_CTS_HMAC_SHA1_96, reply.getTicket().getEncPart()
303                 .getEType() );
304 
305         assertEquals( "Nonce", nonce, reply.getNonce() );
306     }
307 
308 
309     /**
310      * Tests that the default reply key is the session key from the TGT.
311      *
312      * @throws Exception
313      */
314     public void testDecryptWithSessionKey() throws Exception
315     {
316         EncryptionType[] configuredEncryptionTypes =
317                 {EncryptionType.AES128_CTS_HMAC_SHA1_96};
318         config.setEncryptionTypes( configuredEncryptionTypes );
319 
320         // Get the mutable ticket part.
321         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
322         EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
323 
324         // Make changes to test.
325         sessionKey = RandomKeyFactory.getRandomKey( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
326         encTicketPartModifier.setSessionKey( sessionKey );
327 
328         // Seal the ticket for the server.
329         String principalName = "krbtgt/EXAMPLE.COM@EXAMPLE.COM";
330         KerberosPrincipal serverPrincipal = new KerberosPrincipal( principalName );
331         String passPhrase = "randomKey";
332         Set<EncryptionType> preAuthEncryptionTypes = new HashSet<EncryptionType>();
333         preAuthEncryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
334 
335         Map<EncryptionType, EncryptionKey> keyMap = KerberosKeyFactory.getKerberosKeys( principalName, passPhrase,
336                 preAuthEncryptionTypes );
337         EncryptionKey serverKey = keyMap.get( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
338 
339         Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
340 
341         RequestBodyModifier modifier = new RequestBodyModifier();
342         modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
343         modifier.setRealm( "EXAMPLE.COM" );
344 
345         Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
346         encryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
347 
348         modifier.setEType( encryptionTypes );
349 
350         modifier.setNonce( random.nextInt() );
351 
352         KdcOptions kdcOptions = new KdcOptions();
353         modifier.setKdcOptions( kdcOptions );
354 
355         long now = System.currentTimeMillis();
356         KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
357         modifier.setTill( requestedEndTime );
358 
359         RequestBody requestBody = modifier.getRequestBody();
360         KdcRequest message = getKdcRequest( tgt, requestBody );
361 
362         handler.messageReceived( session, message );
363 
364         TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
365 
366         assertEquals( "Encryption type", EncryptionType.AES128_CTS_HMAC_SHA1_96, reply.getEncPart().getEType() );
367         assertEquals( "Encryption type", EncryptionType.AES128_CTS_HMAC_SHA1_96, reply.getTicket().getEncPart()
368                 .getEType() );
369     }
370 
371 
372     /**
373      * Tests when a sub-session key is placed in the Authenticator that the
374      * reply key is the sub-session key and not the TGT session key.
375      *
376      * @throws Exception
377      */
378     public void testDecryptWithSubSessionKey() throws Exception
379     {
380         EncryptionType[] configuredEncryptionTypes =
381                 {EncryptionType.AES128_CTS_HMAC_SHA1_96};
382         config.setEncryptionTypes( configuredEncryptionTypes );
383 
384         // Get the mutable ticket part.
385         KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
386         EncTicketPartModifier encTicketPartModifier = getTicketArchetype( clientPrincipal );
387 
388         // Make changes to test.
389         sessionKey = RandomKeyFactory.getRandomKey( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
390         encTicketPartModifier.setSessionKey( sessionKey );
391 
392         // Seal the ticket for the server.
393         String principalName = "krbtgt/EXAMPLE.COM@EXAMPLE.COM";
394         KerberosPrincipal serverPrincipal = new KerberosPrincipal( principalName );
395         String passPhrase = "randomKey";
396         Set<EncryptionType> preAuthEncryptionTypes = new HashSet<EncryptionType>();
397         preAuthEncryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
398 
399         Map<EncryptionType, EncryptionKey> keyMap = KerberosKeyFactory.getKerberosKeys( principalName, passPhrase,
400                 preAuthEncryptionTypes );
401         EncryptionKey serverKey = keyMap.get( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
402 
403         Ticket tgt = getTicket( encTicketPartModifier, serverPrincipal, serverKey );
404 
405         RequestBodyModifier modifier = new RequestBodyModifier();
406         modifier.setServerName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
407         modifier.setRealm( "EXAMPLE.COM" );
408 
409         Set<EncryptionType> encryptionTypes = new HashSet<EncryptionType>();
410         encryptionTypes.add( EncryptionType.AES128_CTS_HMAC_SHA1_96 );
411 
412         modifier.setEType( encryptionTypes );
413 
414         modifier.setNonce( random.nextInt() );
415 
416         KdcOptions kdcOptions = new KdcOptions();
417         modifier.setKdcOptions( kdcOptions );
418 
419         long now = System.currentTimeMillis();
420         KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
421         modifier.setTill( requestedEndTime );
422 
423         subSessionKey = RandomKeyFactory.getRandomKey( EncryptionType.DES_CBC_MD5 );
424 
425         RequestBody requestBody = modifier.getRequestBody();
426         KdcRequest message = getKdcRequest( tgt, requestBody );
427 
428         handler.messageReceived( session, message );
429 
430         TicketGrantReply reply = ( TicketGrantReply ) session.getMessage();
431 
432         assertEquals( "Encryption type", EncryptionType.DES_CBC_MD5, reply.getEncPart().getEType() );
433         assertEquals( "Encryption type", EncryptionType.AES128_CTS_HMAC_SHA1_96, reply.getTicket().getEncPart()
434                 .getEType() );
435     }
436 }