alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (KeyTab.java)

This example Java source code file (KeyTab.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

arraylist, debug, encryptionkey, fileoutputstream, hashmap, ioexception, keytab, keytabentry, keytabinputstream, keytaboutputstream, krbexception, realmexception, string, stringtokenizer, util

The KeyTab.java Java example source code

/*
 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/*
 *
 *  (C) Copyright IBM Corp. 1999 All Rights Reserved.
 *  Copyright 1997 The Open Group Research Institute.  All rights reserved.
 */

package sun.security.krb5.internal.ktab;

import sun.security.krb5.*;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.Vector;
import sun.security.jgss.krb5.ServiceCreds;

/**
 * This class represents key table. The key table functions deal with storing
 * and retrieving service keys for use in authentication exchanges.
 *
 * A KeyTab object is always constructed, if the file specified does not
 * exist, it's still valid but empty. If there is an I/O error or file format
 * error, it's invalid.
 *
 * The class is immutable on the read side (the write side is only used by
 * the ktab tool).
 *
 * @author Yanni Zhang
 */
public class KeyTab implements KeyTabConstants {

    private static final boolean DEBUG = Krb5.DEBUG;
    private static String defaultTabName = null;

    // Attention: Currently there is no way to remove a keytab from this map,
    // this might lead to a memory leak.
    private static Map<String,KeyTab> map = new HashMap<>();

    // KeyTab file does not exist. Note: a missing keytab is still valid
    private boolean isMissing = false;

    // KeyTab file is invalid, possibly an I/O error or a file format error.
    private boolean isValid = true;

    private final String tabName;
    private long lastModified;
    private int kt_vno = KRB5_KT_VNO;

    private Vector<KeyTabEntry> entries = new Vector<>();

    /**
     * Constructs a KeyTab object.
     *
     * If there is any I/O error or format errot during the loading, the
     * isValid flag is set to false, and all half-read entries are dismissed.
     * @param filename path name for the keytab file, must not be null
     */
    private KeyTab(String filename) {
        tabName = filename;
        try {
            lastModified = new File(tabName).lastModified();
            try (KeyTabInputStream kis =
                    new KeyTabInputStream(new FileInputStream(filename))) {
                load(kis);
            }
        } catch (FileNotFoundException e) {
            entries.clear();
            isMissing = true;
        } catch (Exception ioe) {
            entries.clear();
            isValid = false;
        }
    }

    /**
     * Read a keytab file. Returns a new object and save it into cache when
     * new content (modified since last read) is available. If keytab file is
     * invalid, the old object will be returned. This is a safeguard for
     * partial-written keytab files or non-stable network. Please note that
     * a missing keytab is valid, which is equivalent to an empty keytab.
     *
     * @param s file name of keytab, must not be null
     * @return the keytab object, can be invalid, but never null.
     */
    private synchronized static KeyTab getInstance0(String s) {
        long lm = new File(s).lastModified();
        KeyTab old = map.get(s);
        if (old != null && old.isValid() && old.lastModified == lm) {
            return old;
        }
        KeyTab ktab = new KeyTab(s);
        if (ktab.isValid()) {               // A valid new keytab
            map.put(s, ktab);
            return ktab;
        } else if (old != null) {           // An existing old one
            return old;
        } else {
            return ktab;                    // first read is invalid
        }
    }

    /**
     * Gets a KeyTab object.
     * @param s the key tab file name.
     * @return the KeyTab object, never null.
     */
    public static KeyTab getInstance(String s) {
        if (s == null) {
            return getInstance();
        } else {
            return getInstance0(normalize(s));
        }
    }

    /**
     * Gets a KeyTab object.
     * @param file the key tab file.
     * @return the KeyTab object, never null.
     */
    public static KeyTab getInstance(File file) {
        if (file == null) {
            return getInstance();
        } else {
            return getInstance0(file.getPath());
        }
    }

    /**
     * Gets the default KeyTab object.
     * @return the KeyTab object, never null.
     */
    public static KeyTab getInstance() {
        return getInstance(getDefaultTabName());
    }

    public boolean isMissing() {
        return isMissing;
    }

    public boolean isValid() {
        return isValid;
    }

    /**
     * The location of keytab file will be read from the configuration file
     * If it is not specified, consider user.home as the keytab file's
     * default location.
     * @return never null
     */
    private static String getDefaultTabName() {
        if (defaultTabName != null) {
            return defaultTabName;
        } else {
            String kname = null;
            try {
                String keytab_names = Config.getInstance().get
                        ("libdefaults", "default_keytab_name");
                if (keytab_names != null) {
                    StringTokenizer st = new StringTokenizer(keytab_names, " ");
                    while (st.hasMoreTokens()) {
                        kname = normalize(st.nextToken());
                        if (new File(kname).exists()) {
                            break;
                        }
                    }
                }
            } catch (KrbException e) {
                kname = null;
            }

            if (kname == null) {
                String user_home =
                        java.security.AccessController.doPrivileged(
                        new sun.security.action.GetPropertyAction("user.home"));

                if (user_home == null) {
                    user_home =
                        java.security.AccessController.doPrivileged(
                        new sun.security.action.GetPropertyAction("user.dir"));
                }

                kname = user_home + File.separator  + "krb5.keytab";
            }
            defaultTabName = kname;
            return kname;
        }
    }

    /**
     * Normalizes some common keytab name formats into the bare file name.
     * For example, FILE:/etc/krb5.keytab to /etc/krb5.keytab
     * @param name never null
     * @return never null
     */
    // This method is used in this class and Krb5LoginModule
    public static String normalize(String name) {
        String kname;
        if ((name.length() >= 5) &&
            (name.substring(0, 5).equalsIgnoreCase("FILE:"))) {
            kname = name.substring(5);
        } else if ((name.length() >= 9) &&
                (name.substring(0, 9).equalsIgnoreCase("ANY:FILE:"))) {
            // this format found in MIT's krb5.ini.
            kname = name.substring(9);
        } else if ((name.length() >= 7) &&
                (name.substring(0, 7).equalsIgnoreCase("SRVTAB:"))) {
            // this format found in MIT's krb5.ini.
            kname = name.substring(7);
        } else
            kname = name;
        return kname;
    }

    private void load(KeyTabInputStream kis)
        throws IOException, RealmException {

        entries.clear();
        kt_vno = kis.readVersion();
        if (kt_vno == KRB5_KT_VNO_1) {
            kis.setNativeByteOrder();
        }
        int entryLength = 0;
        KeyTabEntry entry;
        while (kis.available() > 0) {
            entryLength = kis.readEntryLength();
            entry = kis.readEntry(entryLength, kt_vno);
            if (DEBUG) {
                System.out.println(">>> KeyTab: load() entry length: " +
                        entryLength + "; type: " +
                        (entry != null? entry.keyType : 0));
            }
            if (entry != null)
                entries.addElement(entry);
        }
    }

    /**
     * Returns a principal name in this keytab. Used by
     * {@link ServiceCreds#getKKeys()}.
     */
    public PrincipalName getOneName() {
        int size = entries.size();
        return size > 0 ? entries.elementAt(size-1).service : null;
    }

    /**
     * Reads all keys for a service from the keytab file that have
     * etypes that have been configured for use.
     * @param service the PrincipalName of the requested service
     * @return an array containing all the service keys, never null
     */
    public EncryptionKey[] readServiceKeys(PrincipalName service) {
        KeyTabEntry entry;
        EncryptionKey key;
        int size = entries.size();
        ArrayList<EncryptionKey> keys = new ArrayList<>(size);
        if (DEBUG) {
            System.out.println("Looking for keys for: " + service);
        }
        for (int i = size-1; i >= 0; i--) {
            entry = entries.elementAt(i);
            if (entry.service.match(service)) {
                if (EType.isSupported(entry.keyType)) {
                    key = new EncryptionKey(entry.keyblock,
                                        entry.keyType,
                                        new Integer(entry.keyVersion));
                    keys.add(key);
                    if (DEBUG) {
                        System.out.println("Added key: " + entry.keyType +
                            "version: " + entry.keyVersion);
                    }
                } else if (DEBUG) {
                    System.out.println("Found unsupported keytype (" +
                        entry.keyType + ") for " + service);
                }
            }
        }
        size = keys.size();
        EncryptionKey[] retVal = keys.toArray(new EncryptionKey[size]);

        // Sort the keys by kvno. Sometimes we must choose a single key (say,
        // generate encrypted timestamp in AS-REQ). A key with a higher KVNO
        // sounds like a newer one.
        Arrays.sort(retVal, new Comparator<EncryptionKey>() {
            @Override
            public int compare(EncryptionKey o1, EncryptionKey o2) {
                return o2.getKeyVersionNumber().intValue()
                        - o1.getKeyVersionNumber().intValue();
            }
        });

        return retVal;
    }



    /**
     * Searches for the service entry in the keytab file.
     * The etype of the key must be one that has been configured
     * to be used.
     * @param service the PrincipalName of the requested service.
     * @return true if the entry is found, otherwise, return false.
     */
    public boolean findServiceEntry(PrincipalName service) {
        KeyTabEntry entry;
        for (int i = 0; i < entries.size(); i++) {
            entry = entries.elementAt(i);
            if (entry.service.match(service)) {
                if (EType.isSupported(entry.keyType)) {
                    return true;
                } else if (DEBUG) {
                    System.out.println("Found unsupported keytype (" +
                        entry.keyType + ") for " + service);
                }
            }
        }
        return false;
    }

    public String tabName() {
        return tabName;
    }

    /////////////////// THE WRITE SIDE ///////////////////////
    /////////////// only used by ktab tool //////////////////

    /**
     * Adds a new entry in the key table.
     * @param service the service which will have a new entry in the key table.
     * @param psswd the password which generates the key.
     * @param kvno the kvno to use, -1 means automatic increasing
     * @param append false if entries with old kvno would be removed.
     * Note: if kvno is not -1, entries with the same kvno are always removed
     */
    public void addEntry(PrincipalName service, char[] psswd,
            int kvno, boolean append) throws KrbException {
        addEntry(service, service.getSalt(), psswd, kvno, append);
    }

    // Called by KDC test
    public void addEntry(PrincipalName service, String salt, char[] psswd,
            int kvno, boolean append) throws KrbException {

        EncryptionKey[] encKeys = EncryptionKey.acquireSecretKeys(
            psswd, salt);

        // There should be only one maximum KVNO value for all etypes, so that
        // all added keys can have the same KVNO.

        int maxKvno = 0;    // only useful when kvno == -1
        for (int i = entries.size()-1; i >= 0; i--) {
            KeyTabEntry e = entries.get(i);
            if (e.service.match(service)) {
                if (e.keyVersion > maxKvno) {
                    maxKvno = e.keyVersion;
                }
                if (!append || e.keyVersion == kvno) {
                    entries.removeElementAt(i);
                }
            }
        }
        if (kvno == -1) {
            kvno = maxKvno + 1;
        }

        for (int i = 0; encKeys != null && i < encKeys.length; i++) {
            int keyType = encKeys[i].getEType();
            byte[] keyValue = encKeys[i].getBytes();

            KeyTabEntry newEntry = new KeyTabEntry(service,
                            service.getRealm(),
                            new KerberosTime(System.currentTimeMillis()),
                                               kvno, keyType, keyValue);
            entries.addElement(newEntry);
        }
    }

    /**
     * Gets the list of service entries in key table.
     * @return array of <code>KeyTabEntry.
     */
    public KeyTabEntry[] getEntries() {
        KeyTabEntry[] kentries = new KeyTabEntry[entries.size()];
        for (int i = 0; i < kentries.length; i++) {
            kentries[i] = entries.elementAt(i);
        }
        return kentries;
    }

    /**
     * Creates a new default key table.
     */
    public synchronized static KeyTab create()
        throws IOException, RealmException {
        String dname = getDefaultTabName();
        return create(dname);
    }

    /**
     * Creates a new default key table.
     */
    public synchronized static KeyTab create(String name)
        throws IOException, RealmException {

        try (KeyTabOutputStream kos =
                new KeyTabOutputStream(new FileOutputStream(name))) {
            kos.writeVersion(KRB5_KT_VNO);
        }
        return new KeyTab(name);
    }

    /**
     * Saves the file at the directory.
     */
    public synchronized void save() throws IOException {
        try (KeyTabOutputStream kos =
                new KeyTabOutputStream(new FileOutputStream(tabName))) {
            kos.writeVersion(kt_vno);
            for (int i = 0; i < entries.size(); i++) {
                kos.writeEntry(entries.elementAt(i));
            }
        }
    }

    /**
     * Removes entries from the key table.
     * @param service the service <code>PrincipalName.
     * @param etype the etype to match, remove all if -1
     * @param kvno what kvno to remove, -1 for all, -2 for old
     * @return the number of entries deleted
     */
    public int deleteEntries(PrincipalName service, int etype, int kvno) {
        int count = 0;

        // Remember the highest KVNO for each etype. Used for kvno == -2
        Map<Integer,Integer> highest = new HashMap<>();

        for (int i = entries.size()-1; i >= 0; i--) {
            KeyTabEntry e = entries.get(i);
            if (service.match(e.getService())) {
                if (etype == -1 || e.keyType == etype) {
                    if (kvno == -2) {
                        // Two rounds for kvno == -2. In the first round (here),
                        // only find out highest KVNO for each etype
                        if (highest.containsKey(e.keyType)) {
                            int n = highest.get(e.keyType);
                            if (e.keyVersion > n) {
                                highest.put(e.keyType, e.keyVersion);
                            }
                        } else {
                            highest.put(e.keyType, e.keyVersion);
                        }
                    } else if (kvno == -1 || e.keyVersion == kvno) {
                        entries.removeElementAt(i);
                        count++;
                    }
                }
            }
        }

        // Second round for kvno == -2, remove old entries
        if (kvno == -2) {
            for (int i = entries.size()-1; i >= 0; i--) {
                KeyTabEntry e = entries.get(i);
                if (service.match(e.getService())) {
                    if (etype == -1 || e.keyType == etype) {
                        int n = highest.get(e.keyType);
                        if (e.keyVersion != n) {
                            entries.removeElementAt(i);
                            count++;
                        }
                    }
                }
            }
        }
        return count;
    }

    /**
     * Creates key table file version.
     * @param file the key table file.
     * @exception IOException.
     */
    public synchronized void createVersion(File file) throws IOException {
        try (KeyTabOutputStream kos =
                new KeyTabOutputStream(new FileOutputStream(file))) {
            kos.write16(KRB5_KT_VNO);
        }
    }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java KeyTab.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.