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 021package org.apache.directory.server.kerberos.shared.crypto.encryption; 022 023 024import java.security.GeneralSecurityException; 025import java.security.MessageDigest; 026import java.security.spec.AlgorithmParameterSpec; 027import java.util.zip.CRC32; 028 029import javax.crypto.Cipher; 030import javax.crypto.SecretKey; 031import javax.crypto.spec.IvParameterSpec; 032import javax.crypto.spec.SecretKeySpec; 033 034import org.apache.directory.shared.kerberos.exceptions.KerberosException; 035import org.apache.directory.shared.kerberos.codec.types.EncryptionType; 036import org.apache.directory.shared.kerberos.components.EncryptedData; 037import org.apache.directory.shared.kerberos.components.EncryptionKey; 038import org.apache.directory.shared.kerberos.exceptions.ErrorType; 039 040 041/** 042 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 043 */ 044public class DesCbcCrcEncryption extends EncryptionEngine 045{ 046 private static final byte[] iv = new byte[] 047 { ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00, 048 ( byte ) 0x00 }; 049 050 051 public EncryptionType getEncryptionType() 052 { 053 return EncryptionType.DES_CBC_CRC; 054 } 055 056 057 public int getConfounderLength() 058 { 059 return 8; 060 } 061 062 063 public int getChecksumLength() 064 { 065 return 4; 066 } 067 068 069 public byte[] calculateIntegrity( byte[] data, byte[] key, KeyUsage usage ) 070 { 071 CRC32 crc32 = new CRC32(); 072 crc32.update( data ); 073 074 return int2octet( ( int ) crc32.getValue() ); 075 } 076 077 078 private byte[] int2octet( int value ) 079 { 080 byte[] bytes = new byte[4]; 081 int i, shift; 082 083 for ( i = 0, shift = 24; i < 4; i++, shift -= 8 ) 084 { 085 bytes[i] = ( byte ) ( 0xFF & ( value >> shift ) ); 086 } 087 088 return bytes; 089 } 090 091 092 public byte[] getDecryptedData( EncryptionKey key, EncryptedData data, KeyUsage usage ) throws KerberosException 093 { 094 // decrypt the data 095 byte[] decryptedData = decrypt( data.getCipher(), key.getKeyValue() ); 096 097 // extract the old checksum 098 byte[] oldChecksum = new byte[getChecksumLength()]; 099 System.arraycopy( decryptedData, getConfounderLength(), oldChecksum, 0, oldChecksum.length ); 100 101 // zero out the old checksum in the cipher text 102 for ( int i = getConfounderLength(); i < getConfounderLength() + getChecksumLength(); i++ ) 103 { 104 decryptedData[i] = 0; 105 } 106 107 // calculate a new checksum 108 byte[] newChecksum = calculateIntegrity( decryptedData, key.getKeyValue(), usage ); 109 110 // compare checksums 111 if ( !MessageDigest.isEqual( oldChecksum, newChecksum ) ) 112 { 113 throw new KerberosException( ErrorType.KRB_AP_ERR_BAD_INTEGRITY ); 114 } 115 116 // remove leading confounder and checksum 117 return removeLeadingBytes( decryptedData, getConfounderLength(), getChecksumLength() ); 118 } 119 120 121 public EncryptedData getEncryptedData( EncryptionKey key, byte[] plainText, KeyUsage usage ) 122 { 123 // build the ciphertext structure 124 byte[] conFounder = getRandomBytes( getConfounderLength() ); 125 byte[] zeroedChecksum = new byte[getChecksumLength()]; 126 byte[] dataBytes = concatenateBytes( conFounder, concatenateBytes( zeroedChecksum, plainText ) ); 127 byte[] paddedDataBytes = padString( dataBytes ); 128 byte[] checksumBytes = calculateIntegrity( paddedDataBytes, null, usage ); 129 130 // lay the checksum into the ciphertext 131 for ( int i = getConfounderLength(); i < getConfounderLength() + getChecksumLength(); i++ ) 132 { 133 paddedDataBytes[i] = checksumBytes[i - getConfounderLength()]; 134 } 135 136 byte[] encryptedData = encrypt( paddedDataBytes, key.getKeyValue() ); 137 138 return new EncryptedData( getEncryptionType(), key.getKeyVersion(), encryptedData ); 139 } 140 141 142 public byte[] encrypt( byte[] plainText, byte[] keyBytes ) 143 { 144 return processCipher( true, plainText, keyBytes ); 145 } 146 147 148 public byte[] decrypt( byte[] cipherText, byte[] keyBytes ) 149 { 150 return processCipher( false, cipherText, keyBytes ); 151 } 152 153 154 private byte[] processCipher( boolean isEncrypt, byte[] data, byte[] keyBytes ) 155 { 156 try 157 { 158 Cipher cipher = Cipher.getInstance( "DES/CBC/NoPadding" ); 159 SecretKey key = new SecretKeySpec( keyBytes, "DES" ); 160 161 AlgorithmParameterSpec paramSpec = new IvParameterSpec( iv ); 162 163 if ( isEncrypt ) 164 { 165 cipher.init( Cipher.ENCRYPT_MODE, key, paramSpec ); 166 } 167 else 168 { 169 cipher.init( Cipher.DECRYPT_MODE, key, paramSpec ); 170 } 171 172 return cipher.doFinal( data ); 173 } 174 catch ( GeneralSecurityException nsae ) 175 { 176 nsae.printStackTrace(); 177 return null; 178 } 179 } 180}