All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jcifs.pac.kerberos.KerberosEncData Maven / Gradle / Ivy

/*
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package jcifs.pac.kerberos;


import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.DERApplicationSpecific;
import org.bouncycastle.asn1.DERGeneralString;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.DLSequence;

import jcifs.pac.ASN1Util;
import jcifs.pac.PACDecodingException;
import jcifs.util.Encdec;


@SuppressWarnings ( "javadoc" )
public class KerberosEncData {

    private String userRealm;
    private String userPrincipalName;
    private ArrayList userAddresses;
    private List userAuthorizations;


    public KerberosEncData ( byte[] token, Key key ) throws PACDecodingException {
        ASN1InputStream stream = new ASN1InputStream(new ByteArrayInputStream(token));
        DERApplicationSpecific derToken;
        try {
            derToken = ASN1Util.as(DERApplicationSpecific.class, stream);
            if ( !derToken.isConstructed() )
                throw new PACDecodingException("Malformed kerberos ticket");
            stream.close();
        }
        catch ( IOException e ) {
            throw new PACDecodingException("Malformed kerberos ticket", e);
        }

        stream = new ASN1InputStream(new ByteArrayInputStream(derToken.getContents()));
        DLSequence sequence;
        try {
            sequence = ASN1Util.as(DLSequence.class, stream);
            stream.close();
        }
        catch ( IOException e ) {
            throw new PACDecodingException("Malformed kerberos ticket", e);
        }

        Enumeration fields = sequence.getObjects();
        while ( fields.hasMoreElements() ) {
            ASN1TaggedObject tagged = ASN1Util.as(ASN1TaggedObject.class, fields);

            switch ( tagged.getTagNo() ) {
            case 0: // Ticket Flags
                break;
            case 1: // Key
                break;
            case 2: // Realm
                DERGeneralString derRealm = ASN1Util.as(DERGeneralString.class, tagged);
                this.userRealm = derRealm.getString();
                break;
            case 3: // Principal
                DLSequence principalSequence = ASN1Util.as(DLSequence.class, tagged);
                DLSequence nameSequence = ASN1Util.as(DLSequence.class, ASN1Util.as(DERTaggedObject.class, principalSequence, 1));

                StringBuilder nameBuilder = new StringBuilder();
                Enumeration parts = nameSequence.getObjects();
                while ( parts.hasMoreElements() ) {
                    Object part = parts.nextElement();
                    DERGeneralString stringPart = ASN1Util.as(DERGeneralString.class, part);
                    nameBuilder.append(stringPart.getString());
                    if ( parts.hasMoreElements() )
                        nameBuilder.append('/');
                }
                this.userPrincipalName = nameBuilder.toString();
                break;
            case 4: // Transited Encoding
                break;
            case 5: // Kerberos Time
                // DERGeneralizedTime derTime = KerberosUtil.readAs(tagged,
                // DERGeneralizedTime.class);
                break;
            case 6: // Kerberos Time
                // DERGeneralizedTime derTime = KerberosUtil.readAs(tagged,
                // DERGeneralizedTime.class);
                break;
            case 7: // Kerberos Time
                // DERGeneralizedTime derTime = KerberosUtil.readAs(tagged,
                // DERGeneralizedTime.class);
                break;
            case 8: // Kerberos Time
                // DERGeneralizedTime derTime = KerberosUtil.readAs(tagged,
                // DERGeneralizedTime.class);
                break;
            case 9: // Host Addresses
                DLSequence adressesSequence = ASN1Util.as(DLSequence.class, tagged);
                Enumeration adresses = adressesSequence.getObjects();
                while ( adresses.hasMoreElements() ) {
                    DLSequence addressSequence = ASN1Util.as(DLSequence.class, adresses);
                    ASN1Integer addressType = ASN1Util.as(ASN1Integer.class, addressSequence, 0);
                    DEROctetString addressOctets = ASN1Util.as(DEROctetString.class, addressSequence, 1);

                    this.userAddresses = new ArrayList<>();
                    if ( addressType.getValue().intValue() == KerberosConstants.AF_INTERNET ) {
                        InetAddress userAddress = null;
                        try {
                            userAddress = InetAddress.getByAddress(addressOctets.getOctets());
                        }
                        catch ( UnknownHostException e ) {}
                        this.userAddresses.add(userAddress);
                    }
                }
                break;
            case 10: // Authorization Data
                DLSequence authSequence = ASN1Util.as(DLSequence.class, tagged);

                this.userAuthorizations = new ArrayList<>();
                Enumeration authElements = authSequence.getObjects();
                while ( authElements.hasMoreElements() ) {
                    DLSequence authElement = ASN1Util.as(DLSequence.class, authElements);
                    ASN1Integer authType = ASN1Util.as(ASN1Integer.class, ASN1Util.as(DERTaggedObject.class, authElement, 0));
                    DEROctetString authData = ASN1Util.as(DEROctetString.class, ASN1Util.as(DERTaggedObject.class, authElement, 1));

                    this.userAuthorizations.addAll(KerberosAuthData.parse(authType.getValue().intValue(), authData.getOctets(), key));
                }
                break;
            default:
                throw new PACDecodingException("Unknown field " + tagged.getTagNo());
            }
        }
    }


    public static byte[] decrypt ( byte[] data, Key key, int type ) throws GeneralSecurityException {
        Cipher cipher = null;
        byte[] decrypt = null;

        switch ( type ) {
        case KerberosConstants.DES_ENC_TYPE:
            decrypt = decryptDES(data, key, cipher);
            break;
        case KerberosConstants.RC4_ENC_TYPE:
            decrypt = decryptRC4(data, key);
            break;
        default:
            throw new GeneralSecurityException("Unsupported encryption type " + type);
        }
        return decrypt;
    }


    /**
     * @param data
     * @param key
     * @return
     * @throws GeneralSecurityException
     * @throws NoSuchAlgorithmException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     */
    private static byte[] decryptRC4 ( byte[] data, Key key ) throws GeneralSecurityException, NoSuchAlgorithmException, NoSuchPaddingException,
            InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
        Cipher cipher;
        byte[] decrypt;
        byte[] code = new byte[4];
        Encdec.enc_uint32le(2, code, 0);
        byte[] codeHmac = getHmac(code, key.getEncoded());

        byte[] dataChecksum = new byte[KerberosConstants.CHECKSUM_SIZE];
        System.arraycopy(data, 0, dataChecksum, 0, KerberosConstants.CHECKSUM_SIZE);

        byte[] dataHmac = getHmac(dataChecksum, codeHmac);
        SecretKeySpec dataKey = new SecretKeySpec(dataHmac, KerberosConstants.RC4_ALGORITHM);

        cipher = Cipher.getInstance(KerberosConstants.RC4_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, dataKey);

        int plainDataLength = data.length - KerberosConstants.CHECKSUM_SIZE;
        byte[] plainData = cipher.doFinal(data, KerberosConstants.CHECKSUM_SIZE, plainDataLength);

        byte[] plainDataChecksum = getHmac(plainData, codeHmac);
        if ( plainDataChecksum.length >= KerberosConstants.CHECKSUM_SIZE )
            for ( int i = 0; i < KerberosConstants.CHECKSUM_SIZE; i++ )
                if ( plainDataChecksum[ i ] != data[ i ] )
                    throw new GeneralSecurityException("Checksum failed while decrypting.");

        int decryptLength = plainData.length - KerberosConstants.CONFOUNDER_SIZE;
        decrypt = new byte[decryptLength];
        System.arraycopy(plainData, KerberosConstants.CONFOUNDER_SIZE, decrypt, 0, decryptLength);
        return decrypt;
    }


    /**
     * @param data
     * @param key
     * @param cipher
     * @return
     * @throws GeneralSecurityException
     * @throws InvalidKeyException
     * @throws InvalidAlgorithmParameterException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     */
    private static byte[] decryptDES ( byte[] data, Key key, Cipher cipher )
            throws GeneralSecurityException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
        byte[] decrypt;
        try {
            cipher = Cipher.getInstance("DES/CBC/NoPadding");
        }
        catch ( GeneralSecurityException e ) {
            throw new GeneralSecurityException("Checksum failed while decrypting.");
        }
        byte[] ivec = new byte[8];
        IvParameterSpec params = new IvParameterSpec(ivec);

        SecretKeySpec skSpec = new SecretKeySpec(key.getEncoded(), "DES");
        SecretKey sk = skSpec;

        cipher.init(Cipher.DECRYPT_MODE, sk, params);

        byte[] result;
        result = cipher.doFinal(data);

        decrypt = new byte[result.length];
        System.arraycopy(result, 0, decrypt, 0, result.length);

        int tempSize = decrypt.length - 24;

        byte[] output = new byte[tempSize];
        System.arraycopy(decrypt, 24, output, 0, tempSize);

        decrypt = output;
        return decrypt;
    }


    private static byte[] getHmac ( byte[] data, byte[] key ) throws GeneralSecurityException {
        Key macKey = new SecretKeySpec(key.clone(), KerberosConstants.HMAC_ALGORITHM);
        Mac mac = Mac.getInstance(KerberosConstants.HMAC_ALGORITHM);
        mac.init(macKey);
        return mac.doFinal(data);
    }


    public String getUserRealm () {
        return this.userRealm;
    }


    public String getUserPrincipalName () {
        return this.userPrincipalName;
    }


    public ArrayList getUserAddresses () {
        return this.userAddresses;
    }


    public List getUserAuthorizations () {
        return this.userAuthorizations;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy