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 package org.apache.directory.server.kerberos.shared.crypto.encryption; 021 022 023 import java.security.GeneralSecurityException; 024 import java.security.spec.AlgorithmParameterSpec; 025 import java.util.Arrays; 026 027 import javax.crypto.Cipher; 028 import javax.crypto.Mac; 029 import javax.crypto.SecretKey; 030 import javax.crypto.spec.IvParameterSpec; 031 import javax.crypto.spec.SecretKeySpec; 032 033 import org.apache.directory.server.kerberos.shared.crypto.checksum.ChecksumEngine; 034 import org.apache.directory.server.kerberos.shared.crypto.checksum.ChecksumType; 035 import org.apache.directory.server.kerberos.shared.exceptions.ErrorType; 036 import org.apache.directory.server.kerberos.shared.exceptions.KerberosException; 037 import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData; 038 import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey; 039 040 041 /** 042 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 043 * @version $Rev$, $Date$ 044 */ 045 public class Des3CbcSha1KdEncryption extends EncryptionEngine implements ChecksumEngine 046 { 047 private static final byte[] iv = new byte[] 048 { ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, 049 ( byte ) 0x00 }; 050 051 052 public EncryptionType getEncryptionType() 053 { 054 return EncryptionType.DES3_CBC_SHA1_KD; 055 } 056 057 058 public int getConfounderLength() 059 { 060 return 8; 061 } 062 063 064 public int getChecksumLength() 065 { 066 return 20; 067 } 068 069 070 public ChecksumType checksumType() 071 { 072 return ChecksumType.HMAC_SHA1_DES3_KD; 073 } 074 075 076 public byte[] calculateChecksum( byte[] data, byte[] key, KeyUsage usage ) 077 { 078 byte[] Kc = deriveKey( key, getUsageKc( usage ), 64, 168 ); 079 080 return processChecksum( data, Kc ); 081 } 082 083 084 public byte[] calculateIntegrity( byte[] data, byte[] key, KeyUsage usage ) 085 { 086 byte[] Ki = deriveKey( key, getUsageKi( usage ), 64, 168 ); 087 088 return processChecksum( data, Ki ); 089 } 090 091 092 public byte[] getDecryptedData( EncryptionKey key, EncryptedData data, KeyUsage usage ) throws KerberosException 093 { 094 byte[] Ke = deriveKey( key.getKeyValue(), getUsageKe( usage ), 64, 168 ); 095 096 byte[] encryptedData = data.getCipher(); 097 098 // extract the old checksum 099 byte[] oldChecksum = new byte[getChecksumLength()]; 100 System 101 .arraycopy( encryptedData, encryptedData.length - getChecksumLength(), oldChecksum, 0, oldChecksum.length ); 102 103 // remove trailing checksum 104 encryptedData = removeTrailingBytes( encryptedData, 0, getChecksumLength() ); 105 106 // decrypt the data 107 byte[] decryptedData = decrypt( encryptedData, Ke ); 108 109 // remove leading confounder 110 byte[] withoutConfounder = removeLeadingBytes( decryptedData, getConfounderLength(), 0 ); 111 112 // calculate a new checksum 113 byte[] newChecksum = calculateIntegrity( decryptedData, key.getKeyValue(), usage ); 114 115 // compare checksums 116 if ( !Arrays.equals( oldChecksum, newChecksum ) ) 117 { 118 throw new KerberosException( ErrorType.KRB_AP_ERR_BAD_INTEGRITY ); 119 } 120 121 return withoutConfounder; 122 } 123 124 125 public EncryptedData getEncryptedData( EncryptionKey key, byte[] plainText, KeyUsage usage ) 126 { 127 byte[] Ke = deriveKey( key.getKeyValue(), getUsageKe( usage ), 64, 168 ); 128 129 // build the ciphertext structure 130 byte[] conFounder = getRandomBytes( getConfounderLength() ); 131 byte[] paddedPlainText = padString( plainText ); 132 byte[] dataBytes = concatenateBytes( conFounder, paddedPlainText ); 133 byte[] checksumBytes = calculateIntegrity( dataBytes, key.getKeyValue(), usage ); 134 135 //byte[] encryptedData = encrypt( paddedDataBytes, key.getKeyValue() ); 136 byte[] encryptedData = encrypt( dataBytes, Ke ); 137 138 byte[] cipherText = concatenateBytes( encryptedData, checksumBytes ); 139 140 return new EncryptedData( getEncryptionType(), key.getKeyVersion(), cipherText ); 141 } 142 143 144 public byte[] encrypt( byte[] plainText, byte[] keyBytes ) 145 { 146 return processCipher( true, plainText, keyBytes ); 147 } 148 149 150 public byte[] decrypt( byte[] cipherText, byte[] keyBytes ) 151 { 152 return processCipher( false, cipherText, keyBytes ); 153 } 154 155 156 /** 157 * Derived Key = DK(Base Key, Well-Known Constant) 158 * DK(Key, Constant) = random-to-key(DR(Key, Constant)) 159 * DR(Key, Constant) = k-truncate(E(Key, Constant, initial-cipher-state)) 160 */ 161 protected byte[] deriveKey( byte[] baseKey, byte[] usage, int n, int k ) 162 { 163 byte[] result = deriveRandom( baseKey, usage, n, k ); 164 result = randomToKey( result ); 165 166 return result; 167 } 168 169 170 protected byte[] randomToKey( byte[] seed ) 171 { 172 int kBytes = 24; 173 byte[] result = new byte[kBytes]; 174 175 byte[] fillingKey = new byte[0]; 176 177 int pos = 0; 178 179 for ( int i = 0; i < kBytes; i++ ) 180 { 181 if ( pos < fillingKey.length ) 182 { 183 result[i] = fillingKey[pos]; 184 pos++; 185 } 186 else 187 { 188 fillingKey = getBitGroup( seed, i / 8 ); 189 fillingKey = setParity( fillingKey ); 190 pos = 0; 191 result[i] = fillingKey[pos]; 192 pos++; 193 } 194 } 195 196 return result; 197 } 198 199 200 protected byte[] getBitGroup( byte[] seed, int group ) 201 { 202 int srcPos = group * 7; 203 204 byte[] result = new byte[7]; 205 206 System.arraycopy( seed, srcPos, result, 0, 7 ); 207 208 return result; 209 } 210 211 212 protected byte[] setParity( byte[] in ) 213 { 214 byte[] expandedIn = new byte[8]; 215 216 System.arraycopy( in, 0, expandedIn, 0, in.length ); 217 218 setBit( expandedIn, 62, getBit( in, 7 ) ); 219 setBit( expandedIn, 61, getBit( in, 15 ) ); 220 setBit( expandedIn, 60, getBit( in, 23 ) ); 221 setBit( expandedIn, 59, getBit( in, 31 ) ); 222 setBit( expandedIn, 58, getBit( in, 39 ) ); 223 setBit( expandedIn, 57, getBit( in, 47 ) ); 224 setBit( expandedIn, 56, getBit( in, 55 ) ); 225 226 byte[] out = new byte[8]; 227 228 int bitCount = 0; 229 int index = 0; 230 231 for ( int i = 0; i < 64; i++ ) 232 { 233 if ( ( i + 1 ) % 8 == 0 ) 234 { 235 if ( bitCount % 2 == 0 ) 236 { 237 setBit( out, i, 1 ); 238 } 239 240 index++; 241 bitCount = 0; 242 } 243 else 244 { 245 int val = getBit( expandedIn, index ); 246 boolean bit = val > 0; 247 248 if ( bit ) 249 { 250 setBit( out, i, val ); 251 bitCount++; 252 } 253 254 index++; 255 } 256 } 257 258 return out; 259 } 260 261 262 private byte[] processCipher( boolean isEncrypt, byte[] data, byte[] keyBytes ) 263 { 264 try 265 { 266 Cipher cipher = Cipher.getInstance( "DESede/CBC/NoPadding" ); 267 SecretKey key = new SecretKeySpec( keyBytes, "DESede" ); 268 269 AlgorithmParameterSpec paramSpec = new IvParameterSpec( iv ); 270 271 if ( isEncrypt ) 272 { 273 cipher.init( Cipher.ENCRYPT_MODE, key, paramSpec ); 274 } 275 else 276 { 277 cipher.init( Cipher.DECRYPT_MODE, key, paramSpec ); 278 } 279 280 return cipher.doFinal( data ); 281 } 282 catch ( GeneralSecurityException nsae ) 283 { 284 nsae.printStackTrace(); 285 return null; 286 } 287 } 288 289 290 private byte[] processChecksum( byte[] data, byte[] key ) 291 { 292 try 293 { 294 SecretKey sk = new SecretKeySpec( key, "DESede" ); 295 296 Mac mac = Mac.getInstance( "HmacSHA1" ); 297 mac.init( sk ); 298 299 return mac.doFinal( data ); 300 } 301 catch ( GeneralSecurityException nsae ) 302 { 303 nsae.printStackTrace(); 304 return null; 305 } 306 } 307 }