/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* ident "%Z%%M% %I% %E% SMI"
*
* Copyright (c) 1999 by Sun Microsystems, Inc.
* All rights reserved.
*
*/
package com.sun.slp;
import java.util.*;
import java.io.*;
import java.security.*;
import java.security.cert.*;
/**
* The AuthBlock class models both the client and server side
* authentication blocks.
*<p>
* AuthBlocks are agnostic as to which components from a given
* message should be used in authentication. Thus each message
* must provide the correct components in the correct order.
*<p>
* These components are passed via Object[]s. The Object[] elements
* should be in externalized form, and should be ordered as stated
* in the protocol specification for auth blocks. AuthBlocks will
* add the externalized SPI string before the Object[] and the
* externalized timestamp after the vector.
*<p>
* The AuthBlock class provides a number of static convenience
* methods which operate on sets of AuthBlocks. The sets of
* AuthBlocks are stored in Hashtables, keyed by SPIs.
*/
class AuthBlock {
static private String SPI_PROPERTY = "sun.net.slp.SPIs";
/**
* A convenience method for creating a set of auth blocks
* from internal data structures.
*
* @param message The ordered components of the SLP message
* over which the signature should be computed,
* in externalized (byte[]) form.
* @param lifetime The lifetime for this message, in seconds.
* @return A Hashtable of AuthBlocks, one for each SPI, null if no
* SPIs have been configured.
* @exception ServiceLocationException If a key management or crypto
* algorithm provider cannot be
* instantiated, a SYSTEM_ERROR exception
* is thrown.
* @exception IllegalArgumentException If any of the parameters are null
* or empty.
*/
static Hashtable makeAuthBlocks(Object[] message, int lifetime)
throws ServiceLocationException, IllegalArgumentException {
Hashtable spis = getSignAs();
if (spis == null) {
throw new ServiceLocationException(
ServiceLocationException.AUTHENTICATION_FAILED,
"cant_sign", new Object[0]);
}
Hashtable blocks = new Hashtable();
Enumeration spisEnum = spis.keys();
while (spisEnum.hasMoreElements()) {
String spi = (String) spisEnum.nextElement();
int bsd = ((Integer)(spis.get(spi))).intValue();
blocks.put(spi, new AuthBlock(message, spi, bsd, lifetime));
}
return blocks;
}
/**
* A convenience method which creates a Hashtable of auth blocks
* from an input stream.
*
* @param hdr Header of message being parsed out.
* @param message The ordered components of the SLP message
* over which the signature should have been computed,
* in externalized (byte[]) form.
* @param dis Input stream with the auth block bytes queued up as the
* next thing.
* @param nBlocks Number of auth blocks to read.
* @return A Hashtable of AuthBlocks.
* @exception ServiceLocationException If anything goes wrong during
* parsing. If nBlocks is 0, the
* error code is AUTHENTICATION_ABSENT.
* @exception IllegalArgumentException If any of the parameters are null
* or empty.
* @exception IOException If DataInputStream throws it.
*/
static Hashtable makeAuthBlocks(SrvLocHeader hdr,
Object[] message,
DataInputStream dis,
byte nBlocks)
throws ServiceLocationException,
IllegalArgumentException,
IOException {
Hashtable blocks = new Hashtable();
for (byte cnt = 0; cnt < nBlocks; cnt++) {
AuthBlock ab = new AuthBlock(hdr, message, dis);
blocks.put(ab.getSPI(), ab);
}
return blocks;
}
/**
* A convenience method which verifies all auth blocks in the
* input Hashtable.
*
* @param authBlocks A Hashtable containing AuthBlocks.
* @exception ServiceLocationException Thrown if authentication fails,
* with the error code
* ServiceLocationException.AUTHENTICATION_FAILED. If any
* other error occurs during authentication, the
* error code is ServiceLocationException.SYSTEM_ERROR.
* If the signature hasn't been calculated the
* authentication fails.
* @exception IllegalArgumentException If authBlocks is null or empty.
*/
static void verifyAll(Hashtable authBlocks)
throws ServiceLocationException, IllegalArgumentException {
ensureNonEmpty(authBlocks, "authBlocks");
Enumeration blocks = authBlocks.elements();
while (blocks.hasMoreElements()) {
AuthBlock ab = (AuthBlock) blocks.nextElement();
ab.verify();
}
}
/**
* A convenience method which finds the shortest lifetime in a
* set of AuthBlocks.
*
* @param authBlocks A Hashtable containing AuthBlocks.
* @return The shortest lifetime found.
* @exception IllegalArgumentException If authBlocks is null or empty.
*/
static int getShortestLifetime(Hashtable authBlocks)
throws IllegalArgumentException {
ensureNonEmpty(authBlocks, "authBlocks");
Enumeration blocks = authBlocks.elements();
int lifetime = Integer.MAX_VALUE;
while (blocks.hasMoreElements()) {
AuthBlock ab = (AuthBlock) blocks.nextElement();
int abLife = ab.getLifetime();
lifetime = (lifetime < abLife) ? lifetime : abLife;
}
return lifetime;
}
/**
* A convenience method which externalizes a set of AuthBlocks
* into a ByteArrayOutputStream. The number of blocks is NOT
* written onto the stream.
*
* @param hdr Header of message being externalized.
* @param authBlocks A Hashtable containing AuthBlocks.
* @param baos The output stream into which to write.
* @exception ServiceLocationException Thrown if an error occurs during
* output, with PARSE_ERROR error code.
* @exception IllegalArgumentException If any parameters are null, or
* if authBlocks is empty.
*/
static void externalizeAll(SrvLocHeader hdr,
Hashtable authBlocks,
ByteArrayOutputStream baos)
throws ServiceLocationException, IllegalArgumentException {
ensureNonEmpty(authBlocks, "authBlocks");
Enumeration blocks = authBlocks.elements();
while (blocks.hasMoreElements()) {
AuthBlock ab = (AuthBlock) blocks.nextElement();
ab.externalize(hdr, baos);
}
}
/**
* Returns the message parts obtained from the AuthBlock contructor.
* The Object[] will not have been altered. Note that all AuthBlocks
* contain the same message Object[] Object.
*
* @param authBlocks A Hashtable containing AuthBlocks.
* @return This auth block's message components Object[].
* @exception IllegalArgumentException If authBlocks is null or empty.
*/
static Object[] getContents(Hashtable authBlocks)
throws IllegalArgumentException {
ensureNonEmpty(authBlocks, "authBlocks");
Enumeration blocks = authBlocks.elements();
AuthBlock ab = (AuthBlock) blocks.nextElement();
return ab.getMessageParts();
}
/**
* Creates a String describing all auth blocks in authBlocks.
* We dont't use toString() since that would get Hashtable.toString(),
* and we can format it a little prettier.
*
* @param authBlocks A Hashtable containing AuthBlocks.
* @return A String description of all AuthBlocks in this Hashtable
*/
static String desc(Hashtable authBlocks) {
if (authBlocks == null) {
return "null";
}
Enumeration blocks = authBlocks.elements();
int size = authBlocks.size();
String desc = size == 1 ? "1 Auth Block:\n" : size + " Auth Blocks:\n";
int cnt = 0;
while (blocks.hasMoreElements()) {
AuthBlock ab = (AuthBlock) blocks.nextElement();
desc = desc + " " + (cnt++) + ": " + ab.toString();
}
return desc;
}
/**
* Returns the list of SPIs configured with this 'prop', or null
* if the property hasn't been set.
*/
static LinkedList getSPIList(String prop) {
String spiProp = System.getProperty(prop);
if (spiProp == null) {
return null;
}
return commaSeparatedListToLinkedList(spiProp);
}
/**
* Converts a comma-separaterd list in a String to a LinkedList.
*/
static LinkedList commaSeparatedListToLinkedList(String listStr) {
StringTokenizer stk_comma = new StringTokenizer(listStr, ",");
LinkedList answer = new LinkedList();
while (stk_comma.hasMoreTokens()) {
String spi = stk_comma.nextToken();
answer.add(spi);
}
return answer;
}
/**
* Returns true if this principal is someDH, or if this principal's
* cert has been signed by someDN.
*/
static boolean canSignAs(String someDN) throws ServiceLocationException {
X509Certificate myCert = getSignAsCert();
if (myCert == null) {
return false;
}
KeyStore ks = getKeyStore();
if (ks == null) {
return false;
}
X509Certificate cert = getCert(someDN, ks);
return onCertChain(
myCert.getSubjectDN().toString(), cert.getSubjectDN());
}
/**
* Checks if caDN is in ab's equivalency set, i.e. if caDN
* is in ab's cert chain.
*/
static boolean checkEquiv(String caDN, AuthBlock ab) {
// Get cert for input DN
X509Certificate caCert;
try {
KeyStore ks = getKeyStore();
caCert = getCert(caDN, ks);
} catch (Exception e) {
SLPConfig.getSLPConfig().writeLog(
"cant_get_equivalency",
new Object[] {caDN, e.getMessage()});
return false;
}
return ab.inEqSet(caCert.getSubjectDN());
}
/**
* Filters out from auths all auth blocks which have not been
* signed by DNs equivalent to caDN.
*/
static AuthBlock getEquivalentAuth(String caDN, Hashtable authBlocks) {
if (authBlocks.size() == 0) {
return null;
}
// Get cert for input DN
X509Certificate caCert;
try {
KeyStore ks = getKeyStore();
caCert = getCert(caDN, ks);
} catch (Exception e) {
SLPConfig.getSLPConfig().writeLog(
"cant_get_equivalency",
new Object[] { caDN, e.getMessage()});
return null;
}
Enumeration blocks = authBlocks.elements();
while (blocks.hasMoreElements()) {
AuthBlock ab = (AuthBlock) blocks.nextElement();
if (ab.inEqSet(caCert.getSubjectDN())) {
return ab;
}
}
return null;
}
/**
* Gets a list of signing identities. Returns a Hashtable of
* which the keys are SPI strings (DNs) and the values
* are BSD Integers.
*/
static Hashtable getSignAs() throws ServiceLocationException {
X509Certificate cert = getSignAsCert();
Hashtable answer = new Hashtable();
if (cert == null) {
return null;
}
/* derive DN from alias */
String DN = cert.getSubjectDN().toString();
String e_DN = null;
// escape DN
try {
e_DN = ServiceLocationAttribute.escapeAttributeString(DN, false);
} catch (ServiceLocationException e) {
// Shouldn't get here if badTag == false
e_DN = DN;
}
DN = e_DN;
String alg = cert.getPublicKey().getAlgorithm();
int ibsd;
if (alg.equals("DSA")) {
ibsd = 2;
} else if (alg.equals("RSA")) {
ibsd = 1;
} else {
SLPConfig.getSLPConfig().writeLog("bad_alg_for_alias",
new Object[] {alg});
return null;
}
answer.put(DN, new Integer(ibsd));
return answer;
}
/**
* Returns the cert corresponding to our signing alias.
* @@@ change this when AMI goes in to use private AMI interface.
*/
static X509Certificate getSignAsCert() throws ServiceLocationException {
String spiProp = System.getProperty("sun.net.slp.signAs");
if (spiProp == null) {
SLPConfig.getSLPConfig().writeLog(
"no_spis_given", new Object[0]);
return null;
}
/* load key store */
KeyStore ks = getKeyPkg();
StringTokenizer stk_comma = new StringTokenizer(spiProp, ",");
X509Certificate cert = null;
// Can only sign with one alias, so ignore any extras
if (stk_comma.hasMoreTokens()) {
String alias = stk_comma.nextToken();
/* get keypkg for this alias */
cert = getCert(alias, ks);
}
return cert;
}
/**
* Creates a new AuthBlock based on the SPI and message parts.
*
* @param message The ordered components of the SLP message
* over which the signature should be computed,
* in externalized (byte[]) form.
* @param spi The SLP SPI for which to create the auth block.
* @param lifetime The lifetime for this message, in seconds.
* @exception ServiceLocationException If a key management or crypto
* algorithm provider cannot be
* instantiated, a SYSTEM_ERROR exception
* is thrown.
* @exception IllegalArgumentException If any of the parameters are null
* or empty.
*/
AuthBlock(Object[] message, String spi, int bsd, int lifetime)
throws ServiceLocationException, IllegalArgumentException {
ensureNonEmpty(message, "message");
Assert.nonNullParameter(spi, "spi");
// init crypto provider associated with bsd
this.bsd = bsd;
getSecurityProvider(bsd);
this.message = message;
this.spi = spi;
this.lifetime = lifetime;
this.timeStamp = SLPConfig.currentSLPTime() + lifetime;
// Create the signature: create and sign the hash
try {
// @@@ how to sign for different aliases?
sig.initSign(null);
computeHash();
abBytes = sig.sign();
} catch (InvalidKeyException e) { // @@@ will change for AMI
SLPConfig conf = SLPConfig.getSLPConfig();
throw
new IllegalArgumentException(
conf.formatMessage(
"cant_sign_for_spi",
new Object[] {
spi,
e.getMessage() }));
} catch (SignatureException e) {
SLPConfig conf = SLPConfig.getSLPConfig();
throw
new IllegalArgumentException(
conf.formatMessage(
"cant_sign_for_spi",
new Object[] {
spi,
e.getMessage() }));
}
// calculate the length
abLength =
2 + // bsd
2 + // length
4 + // timestamp
spiBytes.length + // externalized SPI string, with length
abBytes.length; // structured auth block
}
/**
* Creates a new AuthBlock from an input stream.
*
* @param hdr The header of the message being parsed.
* @param message The ordered components of the SLP message
* over which the signature should have been computed,
* in externalized (byte[]) form.
* @param dis Input stream with the auth block bytes queued up as the
* next thing.
* @exception ServiceLocationException If anything goes wrong during
* parsing. If nBlocks is 0, the
* error code is AUTHENTICATION_ABSENT.
* @exception IllegalArgumentException If any of the parameters are null
* or empty.
* @exception IOException If DataInputStream throws it.
*/
AuthBlock(SrvLocHeader hdr, Object[] message, DataInputStream dis)
throws ServiceLocationException,
IllegalArgumentException,
IOException {
Assert.nonNullParameter(hdr, "hdr");
ensureNonEmpty(message, "message");
Assert.nonNullParameter(dis, "dis");
this.message = message;
this.eqSet = new HashSet();
// parse in the auth block from the input stream;
// first get the BSD and length
bsd = hdr.getInt(dis);
abLength = hdr.getInt(dis);
int pos = 4; // bsd and length have already been consumed
// get the timestamp
timeStamp = getInt32(dis);
pos += 4;
hdr.nbytes += 4;
// get the SPI
StringBuffer buf = new StringBuffer();
hdr.getString(buf, dis);
spi = buf.toString();
if (spi.length() == 0) {
throw new ServiceLocationException(
ServiceLocationException.PARSE_ERROR,
"no_spi_string",
new Object[0]);
}
pos += (2 + spi.length());
// get the structured auth block
abBytes = new byte[abLength - pos];
dis.readFully(abBytes, 0, abLength - pos);
hdr.nbytes += abBytes.length;
// calculate remaining lifetime from timestamp
long time = timeStamp - SLPConfig.currentSLPTime();
time = time <= Integer.MAX_VALUE ? time : 0; // no crazy values
lifetime = (int) time;
lifetime = lifetime < 0 ? 0 : lifetime;
// Initialize the crypto provider
getSecurityProvider(bsd);
}
/**
* Gets the size of this auth block, after externalization, in bytes.
*
* @return The number of bytes in this auth block.
*/
int nBytes() {
return abLength;
}
/**
* Returns the message parts obtained from the AuthBlock contructor.
* The Object[] will not have been altered.
*
* @return This auth block's message components Object[].
*/
Object[] getMessageParts() {
return message;
}
/**
* Verifies the signature on this auth block.
*
* @exception ServiceLocationException Thrown if authentication fails,
* with the error code
* ServiceLocationException.AUTHENTICATION_FAILED. If any
* other error occurs during authentication, the
* error code is ServiceLocationException.SYSTEM_ERROR.
* If the signature hasn't been calculated, the
* fails.
*/
void verify() throws ServiceLocationException {
// Load the keystore
KeyStore ks = null;
try {
ks = KeyStore.getInstance("amicerts", "SunAMI");
ks.load(null, null);
} catch (Exception e) {
throw
new ServiceLocationException(
ServiceLocationException.AUTHENTICATION_FAILED,
"no_keystore",
new Object[] {e.getMessage()});
}
// Unescape the SPI for cleaner logging
String u_DN = null;
try {
u_DN =
ServiceLocationAttribute.unescapeAttributeString(spi, false);
} catch (ServiceLocationException e) {
u_DN = spi;
}
// get cert for this spi
X509Certificate cert = getCert(spi, ks);
// check cert validity
try {
cert.checkValidity();
} catch (CertificateException e) {
throw new ServiceLocationException(
ServiceLocationException.AUTHENTICATION_FAILED,
"invalid_cert",
new Object[] {u_DN, e.getMessage()});
}
// check the lifetime
if (lifetime == 0) {
throw new ServiceLocationException(
ServiceLocationException.AUTHENTICATION_FAILED,
"timestamp_failure",
new Object[] {u_DN});
}
// make sure this SPI matches up with configured SPIs
try {
checkSPIs(cert, ks);
} catch (GeneralSecurityException e) {
throw new ServiceLocationException(
ServiceLocationException.AUTHENTICATION_FAILED,
"cant_match_spis",
new Object[] {cert.getSubjectDN(), e.getMessage()});
}
// check the signature
try {
sig.initVerify(cert.getPublicKey());
} catch (InvalidKeyException ex) {
throw
new ServiceLocationException(
ServiceLocationException.INTERNAL_SYSTEM_ERROR,
"init_verify_failure",
new Object[] {
u_DN,
ex.getMessage()});
}
computeHash();
ServiceLocationException vex =
new ServiceLocationException(
ServiceLocationException.AUTHENTICATION_FAILED,
"verify_failure",
new Object[] {u_DN});
try {
if (!sig.verify(abBytes))
throw vex;
} catch (SignatureException ex) {
throw vex;
}
}
/**
* Convert the auth block into its on-the-wire format.
*
* @param hdr The header of the message being parsed out.
* @param baos The output stream into which to write.
* @exception ServiceLocationException Thrown if an error occurs during
* output, with PARSE_ERROR error code.
* @exception IllegalArgumentException If any baos is null.
*/
void externalize(SrvLocHeader hdr, ByteArrayOutputStream baos)
throws ServiceLocationException, IllegalArgumentException {
Assert.nonNullParameter(hdr, "hdr");
Assert.nonNullParameter(baos, "baos");
// Lay out the auth block, starting with the BSD
hdr.putInt(bsd, baos);
// write out the length
hdr.putInt(abLength, baos);
// calculate and write out the timestamp
putInt32(timeStamp, baos);
hdr.nbytes += 4;
// write the SPI string
hdr.putString(spi, baos);
// Finish by writting the structured auth block
baos.write(abBytes, 0, abBytes.length);
hdr.nbytes += abBytes.length;
}
/**
* Returns the SPI associated with this auth block.
*
* @return The SLP SPI for this auth block.
*/
String getSPI() {
return spi;
}
/**
* Returns the lifetime computed from this auth block.
*
* @return The lifetime from this auth block.
*/
int getLifetime() {
return lifetime;
}
/**
* Given a BSD, sets this AuthBlock's Signature to the
* right algorithm.
*/
private void getSecurityProvider(int bsd)
throws ServiceLocationException {
String algo = "Unknown BSD";
try {
if (bsd == 2) {
// get DSA/SHA1 provider
algo = "DSA";
sig = Signature.getInstance("SHA/DSA", "SunAMI");
return;
} else if (bsd == 1) {
algo = "MD5/RSA";
sig = Signature.getInstance("MD5/RSA", "SunAMI");
return;
} else if (bsd == 3) {
algo = "Keyed HMAC with MD5";
}
} catch (GeneralSecurityException e) {
// system error -- no such provider
throw new ServiceLocationException(
ServiceLocationException.INTERNAL_SYSTEM_ERROR,
"cant_get_security_provider",
new Object[] {
new Integer(bsd),
algo,
e.getMessage()});
}
// Unknown or unsupported BSD
throw new ServiceLocationException(
ServiceLocationException.INTERNAL_SYSTEM_ERROR,
"cant_get_security_provider",
new Object[] {
new Integer(bsd),
algo,
"Unknown or unsupported BSD"});
}
/**
* throws an IllegalArgumentException if v is null or empty.
* v can be either a Hashtable or a Object[].
*/
static private void ensureNonEmpty(Object v, String param)
throws IllegalArgumentException {
int size = 0;
if (v != null) {
if (v instanceof Object[]) {
size = ((Object[]) v).length;
} else {
// this will force a class cast exception if not a Hashtable
size = ((Hashtable) v).size();
}
}
if (v == null || size == 0) {
SLPConfig conf = SLPConfig.getSLPConfig();
String msg =
conf.formatMessage("null_or_empty_vector",
new Object[] {param});
throw
new IllegalArgumentException(msg);
}
}
/**
* Computes a hash over the SPI String, message componenets,
* and timstamp. Which hash is used depends on which crypto
* provider was installed.
*
* This method assumes that the class variables spi, sig,
* message, and timeStamp have all been initialized. As a side
* effect, it places the externalized SPI String into spiBytes.
*/
private void computeHash() throws ServiceLocationException {
try {
// get the SPI String bytes
ByteArrayOutputStream baosT = new ByteArrayOutputStream();
SrvLocHeader.putStringField(spi, baosT, Defaults.UTF8);
spiBytes = baosT.toByteArray();
sig.update(spiBytes);
// Add each message component
int mSize = message.length;
for (int i = 0; i < mSize; i++) {
sig.update((byte[]) message[i]);
}
// end by adding the timestamp
baosT = new ByteArrayOutputStream();
putInt32(timeStamp, baosT);
sig.update(baosT.toByteArray());
} catch (SignatureException e) {
throw new ServiceLocationException(
ServiceLocationException.INTERNAL_SYSTEM_ERROR,
"cant_compute_hash",
new Object[] {e.getMessage()});
}
}
static private long getInt32(DataInputStream dis) throws IOException {
byte[] bytes = new byte[4];
dis.readFully(bytes, 0, 4);
long a = (long)(bytes[0] & 0xFF);
long b = (long)(bytes[1] & 0xFF);
long c = (long)(bytes[2] & 0xFF);
long d = (long)(bytes[3] & 0xFF);
long i = a << 24;
i += b << 16;
i += c << 8;
i += d;
return i;
}
static private void putInt32(long i, ByteArrayOutputStream baos) {
baos.write((byte) ((i >> 24) & 0xFF));
baos.write((byte) ((i >> 16) & 0xFF));
baos.write((byte) ((i >> 8) & 0xFF));
baos.write((byte) (i & 0XFF));
}
/**
* Determines if this process' SPI configuration allows
* messages signed by 'cert' to be verified. This method
* also verifies and validates 'cert's cert chain.
*/
private void checkSPIs(X509Certificate cert, KeyStore ks)
throws ServiceLocationException, GeneralSecurityException {
// get the list of configured SPIs
String conf_spis = System.getProperty("sun.net.slp.SPIs");
if (conf_spis == null) {
throw new ServiceLocationException(
ServiceLocationException.AUTHENTICATION_FAILED,
"no_spis_configured", new Object[0]);
}
// Get cert chain
java.security.cert.Certificate[] chain =
ks.getCertificateChain(cert.getSubjectDN().toString());
if (chain == null) {
throw new ServiceLocationException(
ServiceLocationException.AUTHENTICATION_FAILED,
"no_cert_chain",
new Object[] {cert.getSubjectDN().toString()});
}
// validate all links in chain
int i = 0;
try {
// Add cert's own subjec to equiv set
eqSet.add(((X509Certificate)chain[0]).getSubjectDN());
for (i = 1; i < chain.length; i++) {
((X509Certificate)chain[i]).checkValidity();
chain[i-1].verify(chain[i].getPublicKey(), "SunAMI");
// OK, so add to equivalency set
eqSet.add(((X509Certificate)chain[i]).getSubjectDN());
}
} catch (ClassCastException e) {
throw new ServiceLocationException(
ServiceLocationException.AUTHENTICATION_FAILED,
"not_x509cert",
new Object[] { chain[i].getType(), e.getMessage() });
}
if (configuredToVerify(chain, conf_spis, ks)) {
return;
}
// if we get here, no SPIs matched, so the authentication fails
throw new ServiceLocationException(
ServiceLocationException.AUTHENTICATION_FAILED,
"cant_match_spis",
new Object[] {cert.getSubjectDN().toString(), ""});
}
/**
* Determines if, given a set of SPIs 'conf_spis', we can
* verify a message signed by the Principal named by 'cert'.
*/
static private boolean configuredToVerify(
java.security.cert.Certificate[] chain,
String conf_spis,
KeyStore ks) {
StringTokenizer stk = new StringTokenizer(conf_spis, ",");
while (stk.hasMoreTokens()) {
String spi;
try {
spi = stk.nextToken();
} catch (NoSuchElementException e) {
break;
}
// get CA cert to get CA Principal
Principal ca;
try {
X509Certificate cacert = getCert(spi, ks);
ca = cacert.getSubjectDN();
} catch (ServiceLocationException e) {
SLPConfig.getSLPConfig().writeLog(
"cant_process_spi",
new Object[] {spi, e.getMessage()});
continue;
}
if (onCertChain(ca, chain)) {
return true;
}
}
return false;
}
/**
* Determines if sub if equivalent to ca by getting sub's cert
* chain and walking the chain looking for ca.
* This routine does not verify the cert chain.
*/
private static boolean onCertChain(String sub, Principal ca)
throws ServiceLocationException {
java.security.cert.Certificate[] chain;
ServiceLocationException ex = new ServiceLocationException(
ServiceLocationException.AUTHENTICATION_UNKNOWN,
"no_cert_chain",
new Object[] {sub});
try {
// Get cert keystore
KeyStore ks = getKeyStore();
// Get cert chain for subject
chain = ks.getCertificateChain(sub);
} catch (KeyStoreException e) {
throw ex;
}
if (chain == null) {
throw ex;
}
// walk the cert chain
return onCertChain(ca, chain);
}
/**
* Operates the same as above, but rather than getting the cert
* chain for sub, uses a given cert chain.
*/
private static boolean onCertChain(Principal ca,
java.security.cert.Certificate[] chain)
{
// walk the cert chain
for (int i = 0; i < chain.length; i++) {
Principal sub = ((X509Certificate)chain[i]).getSubjectDN();
if (ca.equals(sub)) {
return true;
}
}
return false;
}
/**
* Returns true if someDN is in this AuthBlock's equivalence set.
*/
private boolean inEqSet(Principal someDN) {
return eqSet.contains(someDN);
}
/**
* Retrieves from the KeyStore 'ks' the X509Certificate named
* by DN.
*/
static private X509Certificate getCert(String DN, KeyStore ks)
throws ServiceLocationException {
X509Certificate cert = null;
// Unescape DN
try {
DN = ServiceLocationAttribute.unescapeAttributeString(DN, false);
} catch (ServiceLocationException e) {
throw new ServiceLocationException(
ServiceLocationException.PARSE_ERROR,
"spi_parse_error",
new Object[] {DN, e.getMessage()});
}
try {
cert = (X509Certificate)ks.getCertificate(DN);
} catch (ClassCastException e) {
throw new ServiceLocationException(
ServiceLocationException.AUTHENTICATION_FAILED,
"not_x509cert",
new Object[] {cert.getType(), e.getMessage()});
} catch (KeyStoreException e) {
throw new ServiceLocationException(
ServiceLocationException.AUTHENTICATION_FAILED,
"no_cert",
new Object[] {DN, e.getMessage()});
}
if (cert == null) {
throw new ServiceLocationException(
ServiceLocationException.AUTHENTICATION_FAILED,
"no_cert",
new Object[] {DN, "" });
}
return cert;
}
/**
* Gets a handle to the trusted key package for this process.
*/
static private synchronized KeyStore getKeyPkg()
throws ServiceLocationException {
if (keypkg != null) {
return keypkg;
}
/* else load key store */
try {
keypkg = KeyStore.getInstance("amiks", "SunAMI");
keypkg.load(null, null);
} catch (Exception e) {
throw new ServiceLocationException(
ServiceLocationException.AUTHENTICATION_FAILED,
"no_keystore",
new Object[] {e.getMessage()});
}
return keypkg;
}
/**
* Gets a handle to a certificate repository.
*/
static private synchronized KeyStore getKeyStore()
throws ServiceLocationException {
if (keystore != null) {
return keystore;
}
try {
keystore = KeyStore.getInstance("amicerts", "SunAMI");
keystore.load(null, null);
} catch (Exception e) {
throw
new ServiceLocationException(
ServiceLocationException.AUTHENTICATION_FAILED,
"no_keystore",
new Object[] {e.getMessage()});
}
return keystore;
}
public String toString() {
return "SPI=``" + spi + "''\n" +
" BSD=``" + bsd + "''\n" +
" timeStamp=``" + timeStamp + "''\n" +
" AuthBlock bytes=" + abLength + " bytes\n";
}
// Instance variables
int bsd;
String spi;
Object[] message;
int lifetime; // need both: lifetime is for optimization,
long timeStamp; // timeStamp is needed to compute the hash
SrvLocHeader hdr;
Signature sig;
int abLength;
byte[] abBytes;
byte[] spiBytes;
HashSet eqSet; // built only during authblock verification
// cached per process
static private KeyStore keystore; // Certificate repository
static private KeyStore keypkg; // My own keypkg
}