Files
Brainstorm/Common/src/com/common/io/CryptoOutputStream.java
2014-05-30 10:31:51 -07:00

166 lines
4.8 KiB
Java

/*
* 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//