166 lines
4.8 KiB
Java
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// |