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

com.ibm.as400.access.CredentialVault Maven / Gradle / Ivy

The newest version!
///////////////////////////////////////////////////////////////////////////////
//
// JTOpen (IBM Toolbox for Java - OSS version)
//
// Filename:  CredentialVault.java
//
// The source code contained herein is licensed under the IBM Public License
// Version 1.0, which has been approved by the Open Source Initiative.
// Copyright (C) 2009-2009 International Business Machines Corporation and
// others.  All rights reserved.
//
///////////////////////////////////////////////////////////////////////////////

package com.ibm.as400.access;

import java.io.Serializable;
import java.util.Arrays;

/**
 * A vault which holds an authentication credential to one or more IBM i host servers.
 * The type of credential stored varies, depending on the sub-class implementation.
 */
abstract class CredentialVault implements Cloneable, Serializable
{
    static final boolean PASSWORD_TRACE = false;

    // Random number generator for seeds.
    static java.util.Random rng = new java.util.Random();

    /** The credential, which is always encoded before being stored */
    protected byte[] encodedCredential_;

    /* Note:  encodedCredential_ may be null if this is a ProfileToken that can be refreshed */
    /* Any subclass that does not set this must implement the following methods */
    /*  clone 
     *  isEmpty
     *  getClearCredential
     *  storeEncodedUsingExternalSeeds
     *  storeEncodedUsingInternalSeeds
     *  disposeOfCredential
     *  decode
     *  trace
     */
  
    /**
     * A credential is encoded with an algorithm that requires a set of seeds. These
     * seeds can be generated internally or they can be provided by an external
     * source. If they are provided by an external source, then seeds to decode the
     * credential must be provided for us to decode it.
     */
    private boolean externalSeedsWereUsed_;

    /**
     * Constructs a CredentialVault object that does not contain a credential.
     */
    protected CredentialVault() {
        this(null);
    }

    /**
     * Constructs a CredentialVault object that contains the provided credential.
     *
     * @param credential The credential to store within the vault
     */
    protected CredentialVault(byte[] credential)
    {
        if (Trace.traceOn_) Trace.log(Trace.INFORMATION, "CredentialVault(byte[] credential) called");

        if (credential == null)
            encodedCredential_ = null;
        else
        {
            // Never store the credential "as is"
            encodedCredential_ = store(credential);
        }
        
        // We have encoded the credential using internally generated seeds.
        externalSeedsWereUsed_ = false;
    }

    /**
     * Creates and returns a copy of the credential vault.  Implementation of this
     * method is specific to the type of credential vault being copied.
     * Some vaults will copy the credential itself into the new vault,
     * others will not.
     *
     * @return A newly created credential vault that is a copy of this one
     */
    @Override
    public CredentialVault clone()
    {
        if (Trace.traceOn_) Trace.log(Trace.INFORMATION, "CredentialVault clone called");

        CredentialVault vaultClone;
        try {
            vaultClone = (CredentialVault)super.clone();
        }
        catch (CloneNotSupportedException e)
        {
            Trace.log(Trace.ERROR, e);
            throw new InternalErrorException(InternalErrorException.UNEXPECTED_EXCEPTION, e);
        }
        
        synchronized(this)
        {
            if (encodedCredential_ != null)
            {
                byte[] credentialCopy = new byte[encodedCredential_.length];
                System.arraycopy(encodedCredential_, 0, credentialCopy, 0, credentialCopy.length);
                vaultClone.encodedCredential_ = credentialCopy;
            }
            
            vaultClone.externalSeedsWereUsed_ = externalSeedsWereUsed_;
        }
        
        return vaultClone;
    }

    /**
     * Retrieves the type of credential stored in the vault.
     *
     * @return The type of credential stored in the vault.
     * Possible values are:
     * 
    *
  • AS400.AUTHENTICATION_SCHEME_PASSWORD *
  • AS400.AUTHENTICATION_SCHEME_GSS_TOKEN *
  • AS400.AUTHENTICATION_SCHEME_PROFILE_TOKEN *
  • AS400.AUTHENTICATION_SCHEME_IDENTITY_TOKEN *
      */ protected abstract int getType(); /** * Purges the credential inside the vault. All resources consumed by the * credential vault are freed. If this method is invoked and the vault is * already empty, the method simply returns and no exception is thrown. */ protected synchronized void empty() { disposeOfCredential(); } @Override protected void finalize() throws Throwable { try { empty(); } finally { super.finalize(); } } /** * Queries the vault to see if it contains a credential. * * @return true if the vault does not contain a credential, false if it does */ protected boolean isEmpty() { return (encodedCredential_ == null); } /** * Retrieves the unencoded credential from the vault. Note that the * credential will only be "clear" in the sense that any encoding done * internally by the CredentialVault is removed. Any encoding done * externally to the credential will remain intact. * * @return The unencoded credential from the vault. */ protected synchronized byte[] getClearCredential() { if (Trace.traceOn_) Trace.log(Trace.INFORMATION, "CredentialVault.getClearCredential called"); // Make sure we have a credential to give them. if (isEmpty()) { Trace.log(Trace.ERROR, "Credential vault is empty"); throw new ExtendedIllegalStateException("credential", ExtendedIllegalStateException.PROPERTY_NOT_SET); } return resolve(encodedCredential_); } /** * Store the credential, after encoding using the provided external seeds. * Any internal encoding previously done by the CredentialVault will be undone * prior to encoding the credential with the provided external seeds. * * @param firstSeed The first seed to use for encoding * @param secondSeed The second seed to use for encoding */ protected synchronized void storeEncodedUsingExternalSeeds(byte[] firstSeed, byte[] secondSeed) { if (Trace.traceOn_) Trace.log(Trace.INFORMATION, "CredentialVault.storedEncodedUsingExternalSeeds called"); // If the credential was already encoded using externally-supplied seeds, then we must not encode it again. if (externalSeedsWereUsed_) { Trace.log(Trace.ERROR, "Called storeEncodedUsingExternalSeeds() when credential was already encoded using external seeds."); throw new InternalErrorException(InternalErrorException.UNKNOWN); } externalSeedsWereUsed_ = true; if (!isEmpty()) { byte[] clearCredential = getClearCredential(); encodedCredential_ = CredentialVault.encode(firstSeed, secondSeed, clearCredential); clearArray(clearCredential); } } /** * Store the credential, after encoding using internally generated seeds. * The credential is first decoded using the provided external seeds. * The decoded credential will then be encoded using internally generated seeds * and remain in the vault. * * @param firstSeed The first seed to use for decoding * @param secondSeed The second seed to use for decoding */ protected synchronized void storeEncodedUsingInternalSeeds(byte[] firstSeed, byte[] secondSeed) { if (Trace.traceOn_) Trace.log(Trace.INFORMATION, "CredentialVault.storedEncodedUsingInternalSeeds called"); // If the credential was not encoded using externally-supplied seeds, then we cannot decode it. if (!externalSeedsWereUsed_) { Trace.log(Trace.ERROR, "Called storeEncodedUsingInternalSeeds() when credential was not previously encoded using external seeds."); throw new InternalErrorException(InternalErrorException.UNKNOWN); } externalSeedsWereUsed_ = false; if (!isEmpty()) { byte[] decodedBytes = CredentialVault.decode(firstSeed, secondSeed, encodedCredential_); encodedCredential_ = store(decodedBytes); clearArray(decodedBytes); } } /** * Disposes of the credential, thus emptying the vault. */ protected void disposeOfCredential() { if (Trace.traceOn_) Trace.log(Trace.INFORMATION, "CredentialVault.disposeOfCredential called"); encodedCredential_ = null; } /** * Encodes the credential, represented as a array of bytes, using * internally generated random seeds. The provided credential * byte array is never modified by this method. * * @param credential The credential to encode * @return The encoded credential */ protected byte[] store(byte[] credential) { byte[] newAdder = new byte[9]; rng.nextBytes(newAdder); byte[] newMask = new byte[7]; rng.nextBytes(newMask); byte[] infoBytes = encode(newAdder, newMask, credential); byte[] returnBytes = new byte[infoBytes.length + 16]; // The format for the stored bytes is // adder : mask : encodedBytes // First store the adder System.arraycopy(newAdder, 0, returnBytes, 0, 9); // Next store the mask System.arraycopy(newMask, 0, returnBytes, 9, 7); // Finally, store the encoded bytes System.arraycopy(infoBytes, 0, returnBytes, 16, infoBytes.length); if (PASSWORD_TRACE) Trace.log(Trace.DIAGNOSTIC, "AS400 object store, bytes:", returnBytes); return returnBytes; } /** * Decodes and returns the internally encoded credential. * The encoded credential byte array is never modified by this method. * * @return The credential stored in the vault, with all internal encoding removed */ protected synchronized byte[] resolve(byte[] encodedBytes) { if (PASSWORD_TRACE) Trace.log(Trace.DIAGNOSTIC, "AS400 object resolve:", encodedBytes); if (encodedBytes == null) return null; // The first 9 bytes in the array are the adder byte[] adder = new byte[9]; System.arraycopy(encodedBytes, 0, adder, 0, 9); // The next 7 bytes are the mask byte[] mask = new byte[7]; System.arraycopy(encodedBytes, 9, mask, 0, 7); // And everything that is left is the encoded bytes byte[] infoBytes = new byte[encodedBytes.length - 16]; System.arraycopy(encodedBytes, 16, infoBytes, 0, encodedBytes.length - 16); return decode(adder, mask, infoBytes); } /** * Encodes the given credential bytes using the provided adder and mask. * The encoded credential is stored and returned in a newly allocated byte array. * The provided credential byte array is never modified by this method. * * @param adder Used for encoding * @param mask Used for encoding * @param credential The credential to encode, represented as an array of bytes * * @return A newly allocated array of bytes representing the encoded credential */ protected static byte[] encode(byte[] adder, byte[] mask, final byte[] credential) { if (PASSWORD_TRACE) { Trace.log(Trace.DIAGNOSTIC, "AS400 object encode:"); Trace.log(Trace.DIAGNOSTIC, " adder:", adder); Trace.log(Trace.DIAGNOSTIC, " mask:", mask); Trace.log(Trace.DIAGNOSTIC, " bytes:", credential); } if (credential == null) return null; int length = credential.length; byte[] buf = new byte[length]; for (int i = 0; i < length; ++i) { buf[i] = (byte)(credential[i] + adder[i % 9]); } for (int i = 0; i < length; ++i) { buf[i] = (byte)(buf[i] ^ mask[i % 7]); } if (PASSWORD_TRACE) Trace.log(Trace.DIAGNOSTIC, " return:", buf); return buf; } /** * Decodes the given credential bytes using the provided adder and mask. * The decoded credential is returned in a newly allocated byte array. * The provided credential byte array is never modified by this method. * * @param adder Used for encoding * @param mask Used for encoding * @param credential The credential to encode, represented as an array of bytes * * @return A newly allocated array of bytes representing the decoded credential */ protected static byte[] decode(byte[] adder, byte[] mask, byte[] credential) { if (PASSWORD_TRACE) { Trace.log(Trace.DIAGNOSTIC, "AS400 object decode:"); Trace.log(Trace.DIAGNOSTIC, " adder:", adder); Trace.log(Trace.DIAGNOSTIC, " mask:", mask); Trace.log(Trace.DIAGNOSTIC, " bytes:", credential); } int length = credential.length; byte[] buf = new byte[length]; for (int i = 0; i < length; ++i) { buf[i] = (byte)(mask[i % 7] ^ credential[i]); } for (int i = 0; i < length; ++i) { buf[i] = (byte)(buf[i] - adder[i % 9]); } if (PASSWORD_TRACE) Trace.log(Trace.DIAGNOSTIC, " return:", buf); return buf; } /** * Decodes the credential bytes using the provided adder and mask. * The decoded credential is returned in a newly allocated byte array. * * @param adder Used for encoding * @param mask Used for encoding * * @return A newly allocated array of bytes representing the decoded credential */ protected byte[] decode(byte[] adder, byte[] mask) { return CredentialVault.decode(adder, mask, encodedCredential_); } /** * Provides a minimal amount of tracing information about the credential * vault in a nicely formatted string. * * @return The tracing information represented as a string */ protected String trace() { StringBuffer sb = new StringBuffer(); sb.append("type="); sb.append(getType()); sb.append(" : bytes="); Trace.printByteArray(sb, encodedCredential_); return sb.toString(); } public static void clearArray(char[] passwordChars) { if (passwordChars != null && passwordChars.length > 0) Arrays.fill(passwordChars,'\0'); } public static void clearArray(byte[] bytes) { if (bytes != null && bytes.length > 0) { for (int i = 0; i < bytes.length; i++) { bytes[i]=(byte) 0; } } } public static boolean isStarCurrent(char[] password) { return ((password.length == 8) && (password[0]=='*') && ((password[1] == 'C') || (password[1] == 'c')) && ((password[2] == 'U') || (password[2] == 'u')) && ((password[3] == 'R') || (password[3] == 'r')) && ((password[4] == 'R') || (password[4] == 'r')) && ((password[5] == 'E') || (password[5] == 'e')) && ((password[6] == 'N') || (password[6] == 'n')) && ((password[7] == 'T') || (password[7] == 't'))); } public static int getHashCode(char[] password) { int len = password.length; int hashcode = 0; for (int i = 0; i < len; i++) { hashcode =+ password[i] * 31 ^ (len - 1) ; } return hashcode; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy