src.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-jdk8 Show documentation
Show all versions of jt400-jdk8 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;
/**
* 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 */
/* Note: This 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
*/
protected byte[] encodedCredential_;
/**
* 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
*/
public Object 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();
}
// Cleans up the credential information.
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);
}
else {
externalSeedsWereUsed_ = true;
}
if (!isEmpty()) {
encodedCredential_ = CredentialVault.encode(firstSeed, secondSeed, getClearCredential());
}
}
/**
* 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);
}
else {
externalSeedsWereUsed_ = false;
}
if (!isEmpty()) {
encodedCredential_ = store(CredentialVault.decode(firstSeed, secondSeed, encodedCredential_));
}
}
/**
* 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_); // accepts null arg
return sb.toString();
}
public static void clearArray(char[] passwordChars) {
for (int i = 0; i < passwordChars.length; i++) {
passwordChars[i]=(char) i;
}
}
public static boolean isStarCurrent(char[] password) {
if ((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'))) {
return true;
}
return false;
}
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;
}
}