/* * Copyright (c) 1999,2009 Declarative Engineering LLC. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Declarative Engineering LLC * verson 1 which accompanies this distribution, and is available at * http://declarativeengineering.com/legal/DE_Developer_License_v1.txt */ package com.common.io; import java.io.InputStream; import java.io.IOException; import com.common.security.*; /* * This class allows an symmetricly encrypted stream to be plugged into an input stream so that read bytes are decrypted using the private key. */ public abstract class CryptoInputStream extends java.io.InputStream implements IInputStream { protected InputStream stream = null; protected int decryptionCount; protected byte[] encryptedBuffer = null; protected byte[] decryptedBuffer = null; protected int decryptedBufferOffset = 0; protected int decryptionLength = 0; protected boolean stopFlag = false; //Used to notify the read methods that the encrypted segments have ended and that wrappering streams attempting to buffer should be told there are no more bytes at this time.// /** * CryptoInputStream constructor. */ public CryptoInputStream(InputStream stream, IAlgorithm algorithm) { super(); if(stream == null) { throw new IllegalArgumentException("Must provide a valid output stream."); }//if// else if(algorithm == null) { throw new IllegalArgumentException("Must provide a valid algorithm."); }//if// this.stream = stream; }//CryptoInputStream()// /* (non-Javadoc) * @see java.io.InputStream#available() */ public int available() throws IOException { return stream.available(); }//available()// /* (non-Javadoc) * @see com.common.io.IInputStream#canDecrypt() */ public boolean canDecrypt() { return true; }//canDecrypt()// /* (non-Javadoc) * @see java.io.InputStream#close() */ public void close() throws IOException { super.close(); stream.close(); }//close()// /** * Decrypts or verifies (unsigns) data and strips all padding. * @param data The buffer containing encrypted data. * @param dataOffset The index of the first encrypted byte. * @param dataLength The number of encrypted data bytes. * @param output A buffer to contain the decrypted data. * @param outputOffset The location of the first decrypted byte in the output buffer. * @return The number of bytes decrypted. */ protected abstract int decrypt(byte[] data, int dataOffset, int dataLength, byte[] output, int outputOffset); /* (non-Javadoc) * @see com.common.io.IInputStream#decrypt(boolean) */ public void decrypt(boolean decrypt) throws IOException { if(decrypt) { if(decryptionCount == 0) { refillBuffer(); }//if// decryptionCount++; }//if// else { decryptionCount--; if(decryptionCount == 0) { if(stopFlag) { stopFlag = false; }//if// else if((stream.read() & 0xFF) != 0xF0) { throw new java.io.StreamCorruptedException("Error: Premature decryption completion."); }//else if// }//if// }//else// }//decrypt()// /* (non-Javadoc) * @see com.common.io.IInputStream#isDecrypting() */ public boolean isDecrypting() { return decryptionCount > 0; }//isDecrypting()// /* (non-Javadoc) * @see java.io.InputStream#read() */ public int read() throws IOException { int result = -1; if(!stopFlag) { if(decryptionCount > 0) { if(decryptedBufferOffset == decryptionLength) { refillBuffer(); }//if// //The stop flag may be set when we refilled the buffer.// if(!stopFlag) { result = decryptedBuffer[decryptedBufferOffset++]; }//if// }//if// else { result = stream.read(); }//else// }//if// return result; }//read()// /* (non-Javadoc) * @see java.io.InputStream#read(byte[], int, int) */ public int read(byte[] buffer, int offset, int length) throws IOException { int result = -1; if(!stopFlag) { if(decryptionCount > 0) { result = 0; while((!stopFlag) && (result != length)) { int size = 0; if(decryptedBufferOffset == decryptionLength) { refillBuffer(); }//if// //The stop flag may be set when we refilled the buffer.// if(!stopFlag) { size = decryptionLength - decryptedBufferOffset; if(size > length - result) { size = length - result; }//if// System.arraycopy(decryptedBuffer, decryptedBufferOffset, buffer, offset + result, size); result += size; decryptedBufferOffset += size; }//if// }//while// if(result == 0) { result = -1; }//if// }//if// else { result = stream.read(buffer, offset, length); }//else// }//if// return result; }//read()// /* (non-Javadoc) * @see com.common.io.IInputStream#read(java.io.OutputStream, int) */ public int read(java.io.OutputStream out, int length) throws IOException { int result = 0; byte[] bytes = new byte[length]; result = stream.read(bytes, 0, bytes.length); out.write(bytes, 0, result); return result; }//read()// /* (non-Javadoc) * @see java.io.InputStream#read() */ private boolean refillBuffer() throws IOException { boolean result = !stopFlag; if(!stopFlag) { int flag = (stream.read() & 0xFF); if(flag == 0xF0) { //Sometimes a wrappering stream will try to buffer beyond the encrypted segment. In such cases we will stop reading until the decryption flag is turned off.// stopFlag = true; result = false; }//if// else if(flag == 0xFF) { int size = StreamSupport.readInt(stream); int count = stream.read(encryptedBuffer, 0, size); if(count != size) { throw new IOException("Error: Invalid read length."); }//if// decryptionLength = decrypt(encryptedBuffer, 0, count, decryptedBuffer, 0); decryptedBufferOffset = 0; }//else if// else { throw new IOException("Stream corruption found while decrypting."); }//else// }//if// return result; }//refillBuffer()// }//CryptoInputStream//