com.ibm.as400.access.CredentialVault Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jt400 Show documentation
Show all versions of jt400 Show documentation
The Open Source version of the IBM Toolbox for Java
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