/**************************************************************************** * Copyright (c) 2005, 2010 Jan S. Rellermeyer, Systems Group, * Department of Computer Science, ETH Zurich and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Jan S. Rellermeyer - initial API and implementation * Markus Alexander Kuppe - enhancements and bug fixes * *****************************************************************************/ package ch.ethz.iks.slp.impl; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.util.ArrayList; import java.util.List; import ch.ethz.iks.slp.ServiceLocationException; /** * Implementation of the SLP Authentication Block. * * @author Jan S. Rellermeyer, ETH Zurich * @since 0.4 */ class AuthenticationBlock { /** * the BSD code for DSA. */ public static final short BSD_DSA = 0x0002; /** * the timestamp. */ private int timestamp; /** * the data. */ private byte[] data = null; /** * the signature. */ private byte[] sig = null; /** * the SPI. */ private String spi = null; /** * create a new Instance of an AuthenticationBlock. * * @param bsd * the BSD, only BSD_DSA is currently supported. * @param spiStr * the SPI String. * @param timeStamp * the timestamp. * @param byteData * the binary data. * @param signature * the signature, if avaliable. * @throws ServiceLocationException * in case of processing errors. */ AuthenticationBlock(final short bsd, final String spiStr, final int timeStamp, final byte[] byteData, final byte[] signature) throws ServiceLocationException { this(); if (bsd != 0x0002) { throw new ServiceLocationException( ServiceLocationException.NOT_IMPLEMENTED, "Only BSD 0x0002 (DSA) is supported."); } timestamp = timeStamp; data = byteData; spi = spiStr; if (signature == null) { sign(); } else { sig = signature; } } /** * * */ AuthenticationBlock() { } /** * sign the AuthenticationBlock. * * @throws ServiceLocationException * in case of processing errors. */ private void sign() throws ServiceLocationException { try { PrivateKey privateKey = SLPCore.CONFIG.getPrivateKey(spi); SLPCore.platform.logDebug("Signing with SPI: " + spi); Signature signature = Signature.getInstance("SHA1withDSA"); signature.initSign(privateKey); signature.update(data); sig = signature.sign(); } catch (Exception e) { SLPCore.platform.logError(e.getMessage(), e.fillInStackTrace()); throw new ServiceLocationException( ServiceLocationException.AUTHENTICATION_FAILED, "Could not sign data"); } } /** * get the SPI. * * @return the SPI. */ String getSPI() { return spi; } /** * get the timestamp. * * @return the timestamp. */ int getTimestamp() { return timestamp; } /** * verify the authBlock. * * @param verData * the auth data. * @return true if verification suceeds. * @throws ServiceLocationException * in case of IO errors. */ boolean verify(final byte[] verData) throws ServiceLocationException { try { PublicKey publicKey = SLPCore.CONFIG.getPublicKey(spi); Signature signature = Signature.getInstance("SHA1withDSA"); signature.initVerify(publicKey); signature.update(verData); boolean success = signature.verify(sig); SLPCore.platform.logDebug((success ? "Verified with SPI: " : "Verification failed with SPI: ") + spi); return success; } catch (Exception e) { SLPCore.platform.logError(e.getMessage(), e.fillInStackTrace()); throw new ServiceLocationException( ServiceLocationException.AUTHENTICATION_FAILED, "Could not verify data with SPI: " + spi); } } /** * calculates the length of this auth block. * * @return the length. */ int getLength() { return 2 // BSD + 2 // Block length + 4 // timestamp + 2 // spi length + spi.getBytes().length // spi + sig.length; // signature } /** * get the bytes. * * @return the bytes. * @throws IOException * in case of IO errors. */ void write(final DataOutputStream out) throws IOException { out.writeShort(BSD_DSA); // BSD out.writeShort((short) getLength()); out.writeInt(timestamp); byte[] temp = spi.getBytes(); out.writeShort(temp.length); out.write(temp); out.write(sig); } /** * parse a AuthenticationBlock array. * * @param input * the DataInput. * @return a AuthenticationBlock array. * @throws ServiceLocationException * in case of parse / IO errors. * @throws IOException * @throws ServiceLocationException */ static AuthenticationBlock[] parse(final DataInputStream input) throws IOException, ServiceLocationException { List blocks = new ArrayList(); short blockCount = (short) input.readByte(); for (int i = 0; i < blockCount; i++) { AuthenticationBlock authBlock = new AuthenticationBlock(); short bsd = (short) input.readShort(); if (bsd != BSD_DSA) { throw new ServiceLocationException( ServiceLocationException.AUTHENTICATION_FAILED, "BSD " + bsd + " is not supported."); } int size = input.readShort(); authBlock.timestamp = input.readInt(); authBlock.spi = input.readUTF(); authBlock.sig = new byte[size - 2 - 2 - 4 - 2 - authBlock.spi.getBytes().length]; try { input.readFully(authBlock.sig); } catch (IOException ioe) { throw new ServiceLocationException( ServiceLocationException.PARSE_ERROR, ioe.getMessage()); } blocks.add(authBlock); } if (!SLPCore.CONFIG.getSecurityEnabled()) { return new AuthenticationBlock[0]; } return (AuthenticationBlock[]) blocks .toArray(new AuthenticationBlock[0]); } }