209 lines
5.7 KiB
Java
209 lines
5.7 KiB
Java
|
|
/*
|
||
|
|
* 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//
|