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.shared.keytab;
21  
22  
23  import java.security.InvalidKeyException;
24  import java.text.ParseException;
25  import java.text.SimpleDateFormat;
26  import java.util.ArrayList;
27  import java.util.Arrays;
28  import java.util.Date;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.TimeZone;
32  
33  import javax.crypto.spec.DESKeySpec;
34  
35  import junit.framework.TestCase;
36  
37  import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
38  import org.apache.directory.server.kerberos.shared.crypto.encryption.KerberosKeyFactory;
39  import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
40  import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
41  import org.apache.mina.common.ByteBuffer;
42  
43  
44  /**
45   * Tests 'keytab' formatted files.
46   * 
47   * All values are in network byte order.  All text is ASCII.
48   *
49   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
50   * @version $Rev$, $Date$
51   */
52  public class KeytabTest extends TestCase
53  {
54      private static final byte[] keytab1 = new byte[]
55          { ( byte ) 0x05, ( byte ) 0x02, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x3C, ( byte ) 0x00,
56              ( byte ) 0x02, ( byte ) 0x00, ( byte ) 0x0B, ( byte ) 0x45, ( byte ) 0x58, ( byte ) 0x41, ( byte ) 0x4D,
57              ( byte ) 0x50, ( byte ) 0x4C, ( byte ) 0x45, ( byte ) 0x2E, ( byte ) 0x43, ( byte ) 0x4F, ( byte ) 0x4D,
58              ( byte ) 0x00, ( byte ) 0x04, ( byte ) 0x6C, ( byte ) 0x64, ( byte ) 0x61, ( byte ) 0x70, ( byte ) 0x00,
59              ( byte ) 0x10, ( byte ) 0x77, ( byte ) 0x77, ( byte ) 0x77, ( byte ) 0x2E, ( byte ) 0x76, ( byte ) 0x65,
60              ( byte ) 0x72, ( byte ) 0x69, ( byte ) 0x73, ( byte ) 0x69, ( byte ) 0x67, ( byte ) 0x6E, ( byte ) 0x2E,
61              ( byte ) 0x63, ( byte ) 0x6F, ( byte ) 0x6D, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x01,
62              ( byte ) 0x45, ( byte ) 0xD9, ( byte ) 0x60, ( byte ) 0xBE, ( byte ) 0x01, ( byte ) 0x00, ( byte ) 0x03,
63              ( byte ) 0x00, ( byte ) 0x08, ( byte ) 0xD5, ( byte ) 0xE6, ( byte ) 0xC4, ( byte ) 0xD0, ( byte ) 0xFE,
64              ( byte ) 0x25, ( byte ) 0x07, ( byte ) 0x0D };
65  
66      private static final byte[] keytab2 = new byte[]
67          { ( byte ) 0x05, ( byte ) 0x02, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x3C, ( byte ) 0x00,
68              ( byte ) 0x02, ( byte ) 0x00, ( byte ) 0x0B, ( byte ) 0x45, ( byte ) 0x58, ( byte ) 0x41, ( byte ) 0x4D,
69              ( byte ) 0x50, ( byte ) 0x4C, ( byte ) 0x45, ( byte ) 0x2E, ( byte ) 0x43, ( byte ) 0x4F, ( byte ) 0x4D,
70              ( byte ) 0x00, ( byte ) 0x04, ( byte ) 0x48, ( byte ) 0x54, ( byte ) 0x54, ( byte ) 0x50, ( byte ) 0x00,
71              ( byte ) 0x10, ( byte ) 0x77, ( byte ) 0x77, ( byte ) 0x77, ( byte ) 0x2E, ( byte ) 0x76, ( byte ) 0x65,
72              ( byte ) 0x72, ( byte ) 0x69, ( byte ) 0x73, ( byte ) 0x69, ( byte ) 0x67, ( byte ) 0x6E, ( byte ) 0x2E,
73              ( byte ) 0x63, ( byte ) 0x6F, ( byte ) 0x6D, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x01,
74              ( byte ) 0x45, ( byte ) 0xD7, ( byte ) 0x96, ( byte ) 0x79, ( byte ) 0x04, ( byte ) 0x00, ( byte ) 0x03,
75              ( byte ) 0x00, ( byte ) 0x08, ( byte ) 0x13, ( byte ) 0xD9, ( byte ) 0x19, ( byte ) 0x98, ( byte ) 0x23,
76              ( byte ) 0x8F, ( byte ) 0x9E, ( byte ) 0x31 };
77  
78      private static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone( "UTC" );
79  
80      private static final SimpleDateFormat dateFormat = new SimpleDateFormat( "yyyyMMddHHmmss'Z'" );
81  
82      static
83      {
84          dateFormat.setTimeZone( UTC_TIME_ZONE );
85      }
86  
87  
88      /**
89       * Read the first keytab test bytes and check for the presence of a valid DES key.
90       *
91       * @throws Exception
92       */
93      public void testReadKeytab1() throws Exception
94      {
95          Keytab keytab = Keytab.read( keytab1 );
96  
97          assertTrue( "Keytab version", Arrays.equals( Keytab.VERSION_52, keytab.getKeytabVersion() ) );
98          assertEquals( "Entries size", 1, keytab.getEntries().size() );
99  
100         KeytabEntry entry = keytab.getEntries().get( 0 );
101         EncryptionKey key = entry.getKey();
102 
103         try
104         {
105             assertTrue( DESKeySpec.isParityAdjusted( key.getKeyValue(), 0 ) );
106         }
107         catch ( InvalidKeyException ike )
108         {
109             fail( "Key is invalid." );
110         }
111     }
112 
113 
114     /**
115      * Read the second keytab test bytes and check for the presence of a valid DES key.
116      *
117      * @throws Exception
118      */
119     public void testReadKeytab2() throws Exception
120     {
121         Keytab keytab = Keytab.read( keytab2 );
122 
123         assertTrue( "Keytab version", Arrays.equals( Keytab.VERSION_52, keytab.getKeytabVersion() ) );
124         assertEquals( "Entries size", 1, keytab.getEntries().size() );
125 
126         KeytabEntry entry = keytab.getEntries().get( 0 );
127         EncryptionKey key = entry.getKey();
128 
129         try
130         {
131             assertTrue( DESKeySpec.isParityAdjusted( key.getKeyValue(), 0 ) );
132         }
133         catch ( InvalidKeyException ike )
134         {
135             fail( "Key is invalid." );
136         }
137     }
138 
139 
140     /**
141      * Test the writing of a keytab file.
142      *
143      * @throws Exception
144      */
145     public void testWriteKeytab() throws Exception
146     {
147         List<KeytabEntry> entries = new ArrayList<KeytabEntry>();
148 
149         entries.add( getEntry1() );
150         entries.add( getEntry1() );
151 
152         Keytab writer = Keytab.getInstance();
153         writer.setEntries( entries );
154         ByteBuffer buffer = writer.write();
155         assertEquals( "Expected file size.", 130, buffer.limit() );
156     }
157 
158 
159     private KeytabEntry getEntry1() throws ParseException
160     {
161         String principalName = "HTTP/www.verisign.com@EXAMPLE.COM";
162         long principalType = 1;
163 
164         String zuluTime = "20070217235745Z";
165         Date date = null;
166         synchronized ( dateFormat )
167         {
168             date = dateFormat.parse( zuluTime );
169         }
170 
171         KerberosTime timeStamp = new KerberosTime( date );
172 
173         byte keyVersion = 1;
174         String passPhrase = "secret";
175         Map<EncryptionType, EncryptionKey> keys = KerberosKeyFactory.getKerberosKeys( principalName, passPhrase );
176         EncryptionKey key = keys.get( EncryptionType.DES_CBC_MD5 );
177 
178         return new KeytabEntry( principalName, principalType, timeStamp, keyVersion, key );
179     }
180 }