/* * Copyright (c) 2003,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.OutputStream; import java.io.IOException; import com.common.security.*; /* * This class allows an asymmetric stream to be plugged into an output stream so that written bytes are encrypted using the public key. */ public abstract class CryptoOutputStream extends OutputStream implements IOutputStream { protected OutputStream stream = null; protected int encryptionCount; protected byte[] encryptedBuffer = null; protected byte[] decryptedBuffer = null; protected int decryptedBufferOffset = 0; /** * CryptoOutputStream constructor. */ public CryptoOutputStream(OutputStream 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; }//CryptoOutputStream()// /* (non-Javadoc) * @see com.common.io.IOutputStream#canEncrypt() */ public boolean canEncrypt() { return true; }//canEncrypt()// /* (non-Javadoc) * @see java.io.OutputStream#close() */ public void close() throws IOException { while(isEncrypting()) { encrypt(false); }//while// super.close(); stream.close(); }//close()// /** * Empties the buffer containing to-be encrypted data. */ protected void emptyBuffer() throws IOException { if(decryptedBufferOffset > 0) { int size = encrypt(decryptedBuffer, 0, decryptedBufferOffset, encryptedBuffer, 0); //Write the block header.// stream.write(0xFF); //Write the encrypted size.// StreamSupport.writeInt(size, stream); //Write the block data.// stream.write(encryptedBuffer, 0, size); //Reset the block indexes.// decryptedBufferOffset = 0; }//if// }//emptyBuffer()// /** * Encrypts or signs the data. * @param data The buffer containing unencrypted data. * @param dataOffset The index of the first unencrypted byte. * @param dataLength The number of unencrypted data bytes. * @param output A buffer to contain the encrypted data. * @param outputOffset The location of the first encrypted byte in the output buffer. * @return The number of encrypted bytes (includes formatting and padding). */ protected abstract int encrypt(byte[] data, int dataOffset, int dataLength, byte[] output, int outputOffset); /* (non-Javadoc) * @see com.common.io.IOutputStream#encrypt(boolean) */ public void encrypt(boolean encrypt) throws IOException { if(encrypt) { //Flush any unencrypted data before beginning an encrypted segment.// if(encryptionCount == 0) { //TODO: Should flush data from all wrappering streams. This isn't possible with the current "pipe" design pattern for streams.// }//if// encryptionCount++; }//if// else { encryptionCount--; if(encryptionCount == 0) { //TODO: Should flush data from all wrappering streams. This isn't possible with the current "pipe" design pattern for streams.// //Empty the buffer and end the encrypted segment.// emptyBuffer(); stream.write(0xF0); }//if// }//else// }//encrypt()// /* (non-Javadoc) * @see java.io.OutputStream#flush() */ public void flush() throws IOException { /* This could cause a lot of unnecessary padding. if(encryptionCount > 0) { emptyBuffer(); }//if// */ stream.flush(); }//flush()// /* (non-Javadoc) * @see com.common.io.IOutputStream#isEncrypting() */ public boolean isEncrypting() { return encryptionCount > 0; }//isEncrypting()// /* (non-Javadoc) * @see java.io.OutputStream#write(byte[], int, int) */ public void write(byte[] buffer, int offset, int length) throws IOException { if(encryptionCount > 0) { int count = 0; while(count != length) { int size = 0; if(decryptedBufferOffset == decryptedBuffer.length) { emptyBuffer(); }//if// size = decryptedBuffer.length - decryptedBufferOffset; if(size > length - count) { size = length - count; }//if// System.arraycopy(buffer, offset + count, decryptedBuffer, decryptedBufferOffset, size); count += size; decryptedBufferOffset += size; }//while// }//if// else { stream.write(buffer, offset, length); }//else// }//write()// /* (non-Javadoc) * @see java.io.OutputStream#write(int) */ public void write(int b) throws IOException { if(encryptionCount > 0) { if(decryptedBufferOffset == decryptedBuffer.length) { emptyBuffer(); }//if// decryptedBuffer[decryptedBufferOffset++] = (byte) b; }//if// else { stream.write(b); }//else// }//write()// }//CryptoOutputStream//