http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/main/java/org/apache/commons/crypto/stream/input/ChannelInput.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/crypto/stream/input/ChannelInput.java b/src/main/java/org/apache/commons/crypto/stream/input/ChannelInput.java new file mode 100644 index 0000000..ee24a0c --- /dev/null +++ b/src/main/java/org/apache/commons/crypto/stream/input/ChannelInput.java @@ -0,0 +1,165 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.crypto.stream.input; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.ReadableByteChannel; + +/** + * The ChannelInput class takes a <code>ReadableByteChannel</code> object and + * wraps it as <code>Input</code> object acceptable by <code>CryptoInputStream</code>. + */ +public class ChannelInput implements Input { + private static final int SKIP_BUFFER_SIZE = 2048; + + private ByteBuffer buf; + private ReadableByteChannel channel; + + /** + * Constructs the {@link org.apache.commons.crypto.stream.input.ChannelInput}. + * + * @param channel the ReadableByteChannel object. + */ + public ChannelInput( + ReadableByteChannel channel) { + this.channel = channel; + } + + /** + * Overrides the {@link org.apache.commons.crypto.stream.input.Input#read(ByteBuffer)}. + * Reads a sequence of bytes from input into the given buffer. + * + * @param dst The buffer into which bytes are to be transferred. + * @return the total number of bytes read into the buffer, or + * <code>-1</code> if there is no more data because the end of + * the stream has been reached. + * @throws IOException if an I/O error occurs. + */ + @Override + public int read(ByteBuffer dst) throws IOException { + return channel.read(dst); + } + + /** + * Overrides the {@link org.apache.commons.crypto.stream.input.Input#skip(long)}. + * Skips over and discards <code>n</code> bytes of data from this input + * stream. + * + * @param n the number of bytes to be skipped. + * @return the actual number of bytes skipped. + * @throws IOException if an I/O error occurs. + */ + @Override + public long skip(long n) throws IOException { + long remaining = n; + int nr; + + if (n <= 0) { + return 0; + } + + int size = (int)Math.min(SKIP_BUFFER_SIZE, remaining); + ByteBuffer skipBuffer = getSkipBuf(); + while (remaining > 0) { + skipBuffer.clear(); + skipBuffer.limit((int)Math.min(size, remaining)); + nr = read(skipBuffer); + if (nr < 0) { + break; + } + remaining -= nr; + } + + return n - remaining; + } + + /** + * Overrides the {@link Input#available()}. + * Returns an estimate of the number of bytes that can be read (or + * skipped over) from this input stream without blocking by the next + * invocation of a method for this input stream. The next invocation + * might be the same thread or another thread. A single read or skip of this + * many bytes will not block, but may read or skip fewer bytes. + * + * @return an estimate of the number of bytes that can be read (or skipped + * over) from this input stream without blocking or {@code 0} when + * it reaches the end of the input stream. + * @throws IOException if an I/O error occurs. + */ + @Override + public int available() throws IOException { + return 0; + } + + /** + * Overrides the {@link org.apache.commons.crypto.stream.input.Input#read(long, byte[], int, int)}. + * Reads up to <code>len</code> bytes of data from the input stream into + * an array of bytes. An attempt is made to read as many as + * <code>len</code> bytes, but a smaller number may be read. + * The number of bytes actually read is returned as an integer. + * + * @param position the given position within a stream. + * @param buffer the buffer into which the data is read. + * @param offset the start offset in array buffer. + * @param length the maximum number of bytes to read. + * @return the total number of bytes read into the buffer, or + * <code>-1</code> if there is no more data because the end of + * the stream has been reached. + * @throws IOException if an I/O error occurs. + */ + @Override + public int read(long position, byte[] buffer, int offset, int length) + throws IOException { + throw new UnsupportedOperationException( + "Positioned read is not supported by this implementation"); + } + + /** + * Overrides the {@link org.apache.commons.crypto.stream.input.Input#seek(long)}. + * Seeks to the given offset from the start of the stream. + * The next read() will be from that location. + * + * @param position the offset from the start of the stream. + * @throws IOException if an I/O error occurs. + */ + @Override + public void seek(long position) throws IOException { + throw new UnsupportedOperationException( + "Seek is not supported by this implementation"); + } + + /** + * Overrides the {@link org.apache.commons.crypto.stream.input.Input#seek(long)}. + * Closes this input and releases any system resources associated + * with the under layer input. + * + * @throws IOException if an I/O error occurs. + */ + @Override + public void close() throws IOException { + channel.close(); + } + + private ByteBuffer getSkipBuf() { + if (buf == null) { + buf = ByteBuffer.allocate(SKIP_BUFFER_SIZE); + } + return buf; + } +}
http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/main/java/org/apache/commons/crypto/stream/input/Input.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/crypto/stream/input/Input.java b/src/main/java/org/apache/commons/crypto/stream/input/Input.java new file mode 100644 index 0000000..a63c6ca --- /dev/null +++ b/src/main/java/org/apache/commons/crypto/stream/input/Input.java @@ -0,0 +1,133 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.crypto.stream.input; + +import java.io.IOException; +import java.nio.ByteBuffer; + +/** + * The Input interface abstract the input source of <code>CryptoInputStream</code> so that + * different implementation of input can be used. The implementation Input interface will usually + * wraps an input mechanism such as <code>InputStream</code> or <code>ReadableByteChannel</code>. + */ +public interface Input { + /** + * Reads a sequence of bytes from input into the given buffer. + * + * <p> An attempt is made to read up to <i>r</i> bytes from the input, + * where <i>r</i> is the number of bytes remaining in the buffer, that is, + * <tt>dst.remaining()</tt>, at the moment this method is invoked. + * + * <p> Suppose that a byte sequence of length <i>n</i> is read, where + * <tt>0</tt> <tt><=</tt> <i>n</i> <tt><=</tt> <i>r</i>. + * This byte sequence will be transferred into the buffer so that the first + * byte in the sequence is at index <i>p</i> and the last byte is at index + * <i>p</i> <tt>+</tt> <i>n</i> <tt>-</tt> <tt>1</tt>, + * where <i>p</i> is the buffer's position at the moment this method is + * invoked. Upon return the buffer's position will be equal to + * <i>p</i> <tt>+</tt> <i>n</i>; its limit will not have changed. + * + * @param dst + * The buffer into which bytes are to be transferred. + * @return the total number of bytes read into the buffer, or + * <code>-1</code> if there is no more data because the end of + * the stream has been reached. + * @throws IOException + * If some other I/O error occurs. + */ + int read(ByteBuffer dst) throws IOException; + + /** + * Skips over and discards <code>n</code> bytes of data from this input + * The <code>skip</code> method may, for a variety of reasons, end + * up skipping over some smaller number of bytes, possibly <code>0</code>. + * This may result from any of a number of conditions; reaching end of file + * before <code>n</code> bytes have been skipped is only one possibility. + * The actual number of bytes skipped is returned. If <code>n</code> is + * negative, no bytes are skipped. + * + * <p> The <code>skip</code> method of this class creates a + * byte array and then repeatedly reads into it until <code>n</code> bytes + * have been read or the end of the stream has been reached. Subclasses are + * encouraged to provide a more efficient implementation of this method. + * For instance, the implementation may depend on the ability to seek. + * + * @param n the number of bytes to be skipped. + * @return the actual number of bytes skipped. + * @exception IOException if the stream does not support seek, + * or if some other I/O error occurs. + */ + long skip(long n) throws IOException; + + /** + * Returns an estimate of the number of bytes that can be read (or + * skipped over) from this input without blocking by the next + * invocation of a method for this input stream. The next invocation + * might be the same thread or another thread. A single read or skip of this + * many bytes will not block, but may read or skip fewer bytes. + * + * <p> It is never correct to use the return value of this method to allocate + * a buffer intended to hold all data in this stream. + * + * @return an estimate of the number of bytes that can be read (or skipped + * over) from this input stream without blocking or {@code 0} when + * it reaches the end of the input stream. + * @exception IOException if an I/O error occurs. + */ + int available() throws IOException; + + /** + * Reads up to the specified number of bytes from a given position within a + * stream and return the number of bytes read. + * This does not change the current offset of the stream and is thread-safe. + * + * An implementation may not support positioned read. If the implementation + * doesn't support positioned read, it throws UnsupportedOperationException. + * + * @param position the given position within a stream. + * @param buffer the buffer into which the data is read. + * @param offset the start offset in array buffer. + * @param length the maximum number of bytes to read. + * @return the total number of bytes read into the buffer, or + * <code>-1</code> if there is no more data because the end of + * the stream has been reached. + * @throws IOException if an I/O error occurs. + */ + int read(long position, byte[] buffer, int offset, int length) + throws IOException; + + /** + * Seeks to the given offset from the start of the stream. + * The next read() will be from that location. + * + * An implementation may not support seek. If the implementation + * doesn't support seek, it throws UnsupportedOperationException. + * + * @param position the offset from the start of the stream. + * @throws IOException if an I/O error occurs. + */ + void seek(long position) throws IOException; + + /** + * Closes this input and releases any system resources associated + * with the under layer input. + * + * @exception IOException if an I/O error occurs. + */ + void close() throws IOException; +} http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/main/java/org/apache/commons/crypto/stream/input/StreamInput.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/crypto/stream/input/StreamInput.java b/src/main/java/org/apache/commons/crypto/stream/input/StreamInput.java new file mode 100644 index 0000000..ac3739b --- /dev/null +++ b/src/main/java/org/apache/commons/crypto/stream/input/StreamInput.java @@ -0,0 +1,164 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.crypto.stream.input; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; + +/** + * The StreamInput class takes a <code>InputStream</code> object and + * wraps it as <code>Input</code> object acceptable by <code>CryptoInputStream</code>. + */ +public class StreamInput implements Input { + private byte[] buf; + private int bufferSize; + InputStream in; + + /** + * Constructs a {@link org.apache.commons.crypto.stream.input.StreamInput}. + * + * @param inputStream the inputstream object. + * @param bufferSize the buffersize. + */ + public StreamInput(InputStream inputStream, int bufferSize) { + this.in = inputStream; + this.bufferSize = bufferSize; + } + + /** + * Overrides the {@link org.apache.commons.crypto.stream.input.Input#read(ByteBuffer)}. + * Reads a sequence of bytes from input into the given buffer. + * + * @param dst + * The buffer into which bytes are to be transferred. + * + * @return the total number of bytes read into the buffer, or + * <code>-1</code> if there is no more data because the end of + * the stream has been reached. + * @throws IOException if an I/O error occurs. + */ + @Override + public int read(ByteBuffer dst) throws IOException { + int remaining = dst.remaining(); + final byte[] tmp = getBuf(); + int read = 0; + while (remaining > 0) { + final int n = in.read(tmp, 0, Math.min(remaining, bufferSize)); + if (n == -1) { + if (read == 0) { + read = -1; + } + break; + } else if (n > 0) { + dst.put(tmp, 0, n); + read += n; + remaining -= n; + } + } + return read; + } + + /** + * Overrides the {@link org.apache.commons.crypto.stream.input.Input#skip(long)}. + * Skips over and discards <code>n</code> bytes of data from this input + * stream. + * + * @param n the number of bytes to be skipped. + * @return the actual number of bytes skipped. + * @throws IOException if an I/O error occurs. + */ + @Override + public long skip(long n) throws IOException { + return in.skip(n); + } + + /** + * Overrides the {@link Input#available()}. + * Returns an estimate of the number of bytes that can be read (or + * skipped over) from this input stream without blocking by the next + * invocation of a method for this input stream. The next invocation + * might be the same thread or another thread. A single read or skip of this + * many bytes will not block, but may read or skip fewer bytes. + * + * @return an estimate of the number of bytes that can be read (or skipped + * over) from this input stream without blocking or {@code 0} when + * it reaches the end of the input stream. + * @throws IOException if an I/O error occurs. + */ + @Override + public int available() throws IOException { + return in.available(); + } + + /** + * Overrides the {@link org.apache.commons.crypto.stream.input.Input#read(long, byte[], int, int)}. + * Reads up to <code>len</code> bytes of data from the input stream into + * an array of bytes. An attempt is made to read as many as + * <code>len</code> bytes, but a smaller number may be read. + * The number of bytes actually read is returned as an integer. + * + * @param position the given position within a stream. + * @param buffer the buffer into which the data is read. + * @param offset the start offset in array buffer. + * @param length the maximum number of bytes to read. + * @return the total number of bytes read into the buffer, or + * <code>-1</code> if there is no more data because the end of + * the stream has been reached. + * @throws IOException if an I/O error occurs. + */ + @Override + public int read(long position, byte[] buffer, int offset, int length) + throws IOException { + throw new UnsupportedOperationException( + "Positioned read is not supported by this implementation"); + } + + /** + * Overrides the {@link org.apache.commons.crypto.stream.input.Input#seek(long)}. + * Seeks to the given offset from the start of the stream. + * The next read() will be from that location. + * + * @param position the offset from the start of the stream. + * @throws IOException if an I/O error occurs. + */ + @Override + public void seek(long position) throws IOException { + throw new UnsupportedOperationException( + "Seek is not supported by this implementation"); + } + + /** + * Overrides the {@link org.apache.commons.crypto.stream.input.Input#seek(long)}. + * Closes this input and releases any system resources associated + * with the under layer input. + * + * @throws IOException if an I/O error occurs. + */ + @Override + public void close() throws IOException { + in.close(); + } + + private byte[] getBuf() { + if (buf == null) { + buf = new byte[bufferSize]; + } + return buf; + } +} http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/main/java/org/apache/commons/crypto/stream/output/ChannelOutput.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/crypto/stream/output/ChannelOutput.java b/src/main/java/org/apache/commons/crypto/stream/output/ChannelOutput.java new file mode 100644 index 0000000..bae82a8 --- /dev/null +++ b/src/main/java/org/apache/commons/crypto/stream/output/ChannelOutput.java @@ -0,0 +1,78 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.crypto.stream.output; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.WritableByteChannel; + +/** + * The ChannelOutput class takes a <code>WritableByteChannel</code> object and wraps it as + * <code>Output</code> object acceptable by <code>CryptoOutputStream</code> as the output target. + */ +public class ChannelOutput implements Output { + + private WritableByteChannel channel; + + /** + * Constructs a {@link org.apache.commons.crypto.stream.output.ChannelOutput}. + * + * @param channel the WritableByteChannel object. + */ + public ChannelOutput(WritableByteChannel channel) { + this.channel = channel; + } + + /** + * Overrides the {@link org.apache.commons.crypto.stream.output.Output#write(ByteBuffer)}. + * Writes a sequence of bytes to this output from the given buffer. + * + * @param src + * The buffer from which bytes are to be retrieved. + * + * @return The number of bytes written, possibly zero. + * @throws IOException if an I/O error occurs. + */ + @Override + public int write(ByteBuffer src) throws IOException { + return channel.write(src); + } + + /** + * Overrides the {@link Output#flush()}. + * Flushes this output and forces any buffered output bytes + * to be written out if the under layer output method support. + * + * @throws IOException if an I/O error occurs. + */ + @Override + public void flush() throws IOException { + } + + /** + * Overrides the {@link Output#close()}. + * Closes this output and releases any system resources associated + * with the under layer output. + * + * @throws IOException if an I/O error occurs. + */ + @Override + public void close() throws IOException { + channel.close(); + } +} http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/main/java/org/apache/commons/crypto/stream/output/Output.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/crypto/stream/output/Output.java b/src/main/java/org/apache/commons/crypto/stream/output/Output.java new file mode 100644 index 0000000..903fcea --- /dev/null +++ b/src/main/java/org/apache/commons/crypto/stream/output/Output.java @@ -0,0 +1,76 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.crypto.stream.output; + +import java.io.IOException; +import java.nio.ByteBuffer; + +/** + * The Output interface abstract the output target of <code>CryptoOutputStream</code> so that + * different implementation of output can be used. The implementation Output interface will usually + * wraps an output mechanism such as <code>OutputStream</code> or <code>WritableByteChannel</code>. + */ +public interface Output { + + /** + * Writes a sequence of bytes to this output from the given buffer. + * + * <p> An attempt is made to write up to <i>r</i> bytes to the channel, + * where <i>r</i> is the number of bytes remaining in the buffer, that is, + * <tt>src.remaining()</tt>, at the moment this method is invoked. + * + * <p> Suppose that a byte sequence of length <i>n</i> is written, where + * <tt>0</tt> <tt><=</tt> <i>n</i> <tt><=</tt> <i>r</i>. + * This byte sequence will be transferred from the buffer starting at index + * <i>p</i>, where <i>p</i> is the buffer's position at the moment this + * method is invoked; the index of the last byte written will be + * <i>p</i> <tt>+</tt> <i>n</i> <tt>-</tt> <tt>1</tt>. + * Upon return the buffer's position will be equal to + * <i>p</i> <tt>+</tt> <i>n</i>; its limit will not have changed. + * + * @param src + * The buffer from which bytes are to be retrieved. + * + * @return The number of bytes written, possibly zero. + * + * @throws IOException + * If some other I/O error occurs. + */ + int write(ByteBuffer src) throws IOException; + + /** + * Flushes this output and forces any buffered output bytes + * to be written out if the under layer output method support. + * The general contract of <code>flush</code> is + * that calling it is an indication that, if any bytes previously + * written have been buffered by the implementation of the output + * stream, such bytes should immediately be written to their + * intended destination. + * + * @throws IOException if an I/O error occurs. + */ + void flush() throws IOException; + + /** + * Closes this output and releases any system resources associated + * with the under layer output. + * + * @throws IOException if an I/O error occurs. + */ + void close() throws IOException; +} http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/main/java/org/apache/commons/crypto/stream/output/StreamOutput.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/crypto/stream/output/StreamOutput.java b/src/main/java/org/apache/commons/crypto/stream/output/StreamOutput.java new file mode 100644 index 0000000..beead7f --- /dev/null +++ b/src/main/java/org/apache/commons/crypto/stream/output/StreamOutput.java @@ -0,0 +1,101 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.crypto.stream.output; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; + +/** + * The StreamOutput class takes a <code>OutputStream</code> object and wraps it as + * <code>Output</code> object acceptable by <code>CryptoOutputStream</code> as the output target. + */ +public class StreamOutput implements Output { + private byte[] buf; + private int bufferSize; + protected OutputStream out; + + /** + * Constructs a {@link org.apache.commons.crypto.stream.output.StreamOutput}. + * + * @param out the OutputStream object. + * @param bufferSize the buffersize. + */ + public StreamOutput(OutputStream out, int bufferSize) { + this.out = out; + this.bufferSize = bufferSize; + } + + /** + * Overrides the {@link org.apache.commons.crypto.stream.output.Output#write(ByteBuffer)}. + * Writes a sequence of bytes to this output from the given buffer. + * + * @param src + * The buffer from which bytes are to be retrieved. + * + * @return The number of bytes written, possibly zero. + * @throws IOException if an I/O error occurs. + */ + @Override + public int write(ByteBuffer src) throws IOException { + final int len = src.remaining(); + final byte[] buf = getBuf(); + + int remaining = len; + while(remaining > 0) { + final int n = Math.min(remaining, bufferSize); + src.get(buf, 0, n); + out.write(buf, 0, n); + remaining = src.remaining(); + } + + return len; + } + + /** + * Overrides the {@link Output#flush()}. + * Flushes this output and forces any buffered output bytes + * to be written out if the under layer output method support. + * + * @throws IOException if an I/O error occurs. + */ + @Override + public void flush() throws IOException { + out.flush(); + } + + /** + * Overrides the {@link Output#close()}. + * Closes this output and releases any system resources associated + * with the under layer output. + * + * @throws IOException if an I/O error occurs. + */ + @Override + public void close() throws IOException { + out.close(); + } + + private byte[] getBuf() { + if (buf == null) { + buf = new byte[bufferSize]; + } + return buf; + } + +} http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/main/java/org/apache/commons/crypto/utils/IOUtils.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/crypto/utils/IOUtils.java b/src/main/java/org/apache/commons/crypto/utils/IOUtils.java new file mode 100644 index 0000000..58a20a5 --- /dev/null +++ b/src/main/java/org/apache/commons/crypto/utils/IOUtils.java @@ -0,0 +1,96 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.crypto.utils; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.commons.crypto.stream.input.Input; +import org.apache.commons.logging.Log; + +/** + * General utility methods for working with IO. + */ +public class IOUtils { + + /** + * Does the readFully based on the Input read. + * + * @param in the input stream of bytes. + * @param buf the buffer to be read. + * @param off the start offset in array buffer. + * @param len the maximum number of bytes to read. + * @throws IOException if an I/O error occurs. + */ + public static void readFully(InputStream in, byte buf[], + int off, int len) throws IOException { + int toRead = len; + while (toRead > 0) { + int ret = in.read(buf, off, toRead); + if (ret < 0) { + throw new IOException( "Premature EOF from inputStream"); + } + toRead -= ret; + off += ret; + } + } + + /** + * Does the readFully based on Input's positioned read. + * This does not change the current offset of the stream and is thread-safe. + * + * @param in the input source. + * @param position the given position. + * @param buffer the buffer to be read. + * @param length the maximum number of bytes to read. + * @param offset the start offset in array buffer. + * @throws IOException if an I/O error occurs. + */ + public static void readFully(Input in, long position, + byte[] buffer, int offset, int length) throws IOException { + int nread = 0; + while (nread < length) { + int nbytes = in.read(position+nread, buffer, offset+nread, length-nread); + if (nbytes < 0) { + throw new IOException("End of stream reached before reading fully."); + } + nread += nbytes; + } + } + + /** + * Closes the Closeable objects and <b>ignore</b> any {@link IOException} or + * null pointers. Must only be used for cleanup in exception handlers. + * + * @param log the log to record problems to at debug level. Can be null. + * @param closeables the objects to close. + */ + public static void cleanup(Log log, java.io.Closeable... closeables) { + for (java.io.Closeable c : closeables) { + if (c != null) { + try { + c.close(); + } catch(Throwable e) { + if (log != null && log.isDebugEnabled()) { + log.debug("Exception in closing " + c, e); + } + } + } + } + } +} http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/main/java/org/apache/commons/crypto/utils/NativeCodeLoader.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/crypto/utils/NativeCodeLoader.java b/src/main/java/org/apache/commons/crypto/utils/NativeCodeLoader.java new file mode 100644 index 0000000..475fdda --- /dev/null +++ b/src/main/java/org/apache/commons/crypto/utils/NativeCodeLoader.java @@ -0,0 +1,279 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.crypto.utils; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Properties; +import java.util.UUID; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + + +/** + * A helper to load the native code i.e. libchimera.so. + * This handles the fallback to either the bundled libchimera-Linux-i386-32.so + * or the default java implementations where appropriate. + */ +public class NativeCodeLoader { + + private static final Log LOG = + LogFactory.getLog(NativeCodeLoader.class); + + private static boolean nativeCodeLoaded = false; + + static { + // Try to load native library and set fallback flag appropriately + if(LOG.isDebugEnabled()) { + LOG.debug("Trying to load the custom-built native-chimera library..."); + } + + try { + File nativeLibFile = findNativeLibrary(); + if (nativeLibFile != null) { + // Load extracted or specified native library. + System.load(nativeLibFile.getAbsolutePath()); + } else { + // Load preinstalled library (in the path -Djava.library.path) + System.loadLibrary("chimera"); + } + LOG.debug("Loaded the native library"); + nativeCodeLoaded = true; + } catch (Throwable t) { + // Ignore failure to load + if(LOG.isDebugEnabled()) { + LOG.debug("Failed to load native library with error: " + t); + LOG.debug("java.library.path=" + + System.getProperty("java.library.path")); + } + } + + if (!nativeCodeLoaded) { + LOG.warn("Unable to load native library for the platform... " + + "using builtin-java classes where applicable"); + } + } + + static File findNativeLibrary() { + // Try to load the library in chimera.lib.path */ + String nativeLibraryPath = Utils + .getLibPath(); + String nativeLibraryName = Utils + .getLibName(); + + // Resolve the library file name with a suffix (e.g., dll, .so, etc.) + if (nativeLibraryName == null) + nativeLibraryName = System.mapLibraryName("chimera"); + + if (nativeLibraryPath != null) { + File nativeLib = new File(nativeLibraryPath, + nativeLibraryName); + if (nativeLib.exists()) + return nativeLib; + } + + // Load an OS-dependent native library inside a jar file + nativeLibraryPath = "/org/apache/commons/crypto/native/" + + OSInfo.getNativeLibFolderPathForCurrentOS(); + boolean hasNativeLib = hasResource(nativeLibraryPath + "/" + + nativeLibraryName); + if(!hasNativeLib) { + if (OSInfo.getOSName().equals("Mac")) { + // Fix for openjdk7 for Mac + String altName = "libchimera.jnilib"; + if (hasResource(nativeLibraryPath + "/" + altName)) { + nativeLibraryName = altName; + hasNativeLib = true; + } + } + } + + if (!hasNativeLib) { + String errorMessage = String.format( + "no native library is found for os.name=%s and os.arch=%s", + OSInfo.getOSName(), OSInfo.getArchName()); + throw new RuntimeException(errorMessage); + } + + // Temporary folder for the native lib. Use the value of + // chimera.tempdir or java.io.tmpdir + String tempFolder = new File(Utils.getTmpDir()) + .getAbsolutePath(); + + // Extract and load a native library inside the jar file + return extractLibraryFile(nativeLibraryPath, + nativeLibraryName, tempFolder); + } + + /** + * Extracts the specified library file to the target folder. + * + * @param libFolderForCurrentOS the library in chimera.lib.path. + * @param libraryFileName the library name. + * @param targetFolder Target folder for the native lib. Use the value of + * chimera.tempdir or java.io.tmpdir. + * @return the library file. + */ + private static File extractLibraryFile(String libFolderForCurrentOS, + String libraryFileName, String targetFolder) { + String nativeLibraryFilePath = libFolderForCurrentOS + "/" + + libraryFileName; + + // Attach UUID to the native library file to ensure multiple class loaders + // can read the libchimera multiple times. + String uuid = UUID.randomUUID().toString(); + String extractedLibFileName = String.format("chimera-%s-%s-%s", getVersion(), uuid, + libraryFileName); + File extractedLibFile = new File(targetFolder, extractedLibFileName); + + InputStream reader = null; + try { + // Extract a native library file into the target directory + reader = NativeCodeLoader.class.getResourceAsStream(nativeLibraryFilePath); + FileOutputStream writer = new FileOutputStream(extractedLibFile); + try { + byte[] buffer = new byte[8192]; + int bytesRead; + while ((bytesRead = reader.read(buffer)) != -1) { + writer.write(buffer, 0, bytesRead); + } + } finally { + // Delete the extracted lib file on JVM exit. + extractedLibFile.deleteOnExit(); + + if (writer != null){ + writer.close(); + } + + if (reader != null) { + reader.close(); + reader = null; + } + } + + // Set executable (x) flag to enable Java to load the native library + if (!extractedLibFile.setReadable(true) || !extractedLibFile.setExecutable(true) + || !extractedLibFile.setWritable(true, true)) { + throw new RuntimeException("Invalid path for library path"); + } + + // Check whether the contents are properly copied from the resource folder + { + InputStream nativeIn = null; + InputStream extractedLibIn = null; + try { + nativeIn = + NativeCodeLoader.class.getResourceAsStream(nativeLibraryFilePath); + extractedLibIn = new FileInputStream(extractedLibFile); + if (!contentsEquals(nativeIn, extractedLibIn)) + throw new RuntimeException(String.format( + "Failed to write a native library file at %s", + extractedLibFile)); + } finally { + if (nativeIn != null) + nativeIn.close(); + if (extractedLibIn != null) + extractedLibIn.close(); + } + } + + return new File(targetFolder, extractedLibFileName); + } catch (IOException e) { + e.printStackTrace(System.err); + return null; + } finally{ + if(reader != null){ + try { + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + /** + * Gets the version by reading pom.properties embedded in jar. + * This version data is used as a suffix of a dll file extracted from the + * jar. + * + * @return the version string + */ + public static String getVersion() { + URL versionFile = NativeCodeLoader.class + .getResource("/META-INF/maven/org.apache.commons.crypto/chimera/pom.properties"); + if (versionFile == null) + versionFile = NativeCodeLoader.class + .getResource("/org/apache/commons/crypto/VERSION"); + + String version = "unknown"; + try { + if (versionFile != null) { + Properties versionData = new Properties(); + versionData.load(versionFile.openStream()); + version = versionData.getProperty("version", version); + if (version.equals("unknown")) + version = versionData.getProperty("VERSION", version); + version = version.trim().replaceAll("[^0-9M\\.]", ""); + } + } catch (IOException e) { + System.err.println(e); + } + return version; + } + + private static boolean contentsEquals(InputStream in1, InputStream in2) + throws IOException { + if (!(in1 instanceof BufferedInputStream)) { + in1 = new BufferedInputStream(in1); + } + if (!(in2 instanceof BufferedInputStream)) { + in2 = new BufferedInputStream(in2); + } + + int ch = in1.read(); + while (ch != -1) { + int ch2 = in2.read(); + if (ch != ch2) + return false; + ch = in1.read(); + } + int ch2 = in2.read(); + return ch2 == -1; + } + + private static boolean hasResource(String path) { + return NativeCodeLoader.class.getResource(path) != null; + } + + /** + * Checks whether native code is loaded for this platform. + * + * @return <code>true</code> if native is loaded, + * else <code>false</code>. + */ + public static boolean isNativeCodeLoaded() { + return nativeCodeLoaded; + } +} http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/main/java/org/apache/commons/crypto/utils/OSInfo.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/crypto/utils/OSInfo.java b/src/main/java/org/apache/commons/crypto/utils/OSInfo.java new file mode 100644 index 0000000..67f7d56 --- /dev/null +++ b/src/main/java/org/apache/commons/crypto/utils/OSInfo.java @@ -0,0 +1,190 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.crypto.utils; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Locale; + +/** + * Provides OS name and architecture name. + */ +public class OSInfo { + private static HashMap<String, String> archMapping = new HashMap<String, String>(); + + /** + * The constant string represents for X86 architecture, the value is: {@value #X86}.*/ + public static final String X86 = "x86"; + + /** + * The constant string represents for X86_64 architecture, the value is:{@value #X86_64}.*/ + public static final String X86_64 = "x86_64"; + + /** + * The constant string represents for IA64_32 architecture, the value is:{@value #IA64_32}.*/ + public static final String IA64_32 = "ia64_32"; + + /** + * The constant string represents for IA64 architecture, the value is:{@value #IA64}.*/ + public static final String IA64 = "ia64"; + + /** + * The constant string represents for PPC architecture, the value is:{@value #PPC}.*/ + public static final String PPC = "ppc"; + + /** + * The constant string represents for PPC64 architecture, the value is:{@value #PPC64}.*/ + public static final String PPC64 = "ppc64"; + + static { + // x86 mappings + archMapping.put(X86, X86); + archMapping.put("i386", X86); + archMapping.put("i486", X86); + archMapping.put("i586", X86); + archMapping.put("i686", X86); + archMapping.put("pentium", X86); + + // x86_64 mappings + archMapping.put(X86_64, X86_64); + archMapping.put("amd64", X86_64); + archMapping.put("em64t", X86_64); + archMapping.put("universal", X86_64); // Needed for openjdk7 in Mac + + // Itenium 64-bit mappings + archMapping.put(IA64, IA64); + archMapping.put("ia64w", IA64); + + // Itenium 32-bit mappings, usually an HP-UX construct + archMapping.put(IA64_32, IA64_32); + archMapping.put("ia64n", IA64_32); + + // PowerPC mappings + archMapping.put(PPC, PPC); + archMapping.put("power", PPC); + archMapping.put("powerpc", PPC); + archMapping.put("power_pc", PPC); + archMapping.put("power_rs", PPC); + + // TODO: PowerPC 64bit mappings + archMapping.put(PPC64, PPC64); + archMapping.put("power64", PPC64); + archMapping.put("powerpc64", PPC64); + archMapping.put("power_pc64", PPC64); + archMapping.put("power_rs64", PPC64); + } + + public static void main(String[] args) { + if (args.length >= 1) { + if ("--os".equals(args[0])) { + System.out.print(getOSName()); + return; + } else if ("--arch".equals(args[0])) { + System.out.print(getArchName()); + return; + } + } + + System.out.print(getNativeLibFolderPathForCurrentOS()); + } + + /** + * Gets the native lib folder. + * + * @return the current OS's native lib folder. + */ + public static String getNativeLibFolderPathForCurrentOS() { + return getOSName() + "/" + getArchName(); + } + + /** + * Gets the OS name. + * + * @return the OS name. + */ + public static String getOSName() { + return translateOSNameToFolderName(System.getProperty("os.name")); + } + + /** + * Gets the architecture name. + * + * @return the architecture name. + */ + public static String getArchName() { + // if running Linux on ARM, need to determine ABI of JVM + String osArch = System.getProperty("os.arch"); + if (osArch.startsWith("arm") + && System.getProperty("os.name").contains("Linux")) { + String javaHome = System.getProperty("java.home"); + try { + // determine if first JVM found uses ARM hard-float ABI + String[] cmdarray = { + "/bin/sh", + "-c", + "find '" + javaHome + + "' -name 'libjvm.so' | head -1 | xargs readelf -A | " + + "grep 'Tag_ABI_VFP_args: VFP registers'" }; + int exitCode = Runtime.getRuntime().exec(cmdarray).waitFor(); + if (exitCode == 0) + return "armhf"; + } catch (IOException e) { + // ignored: fall back to "arm" arch (soft-float ABI) + } catch (InterruptedException e) { + // ignored: fall back to "arm" arch (soft-float ABI) + } + } else { + String lc = osArch.toLowerCase(Locale.US); + if (archMapping.containsKey(lc)) + return archMapping.get(lc); + } + return translateArchNameToFolderName(osArch); + } + + /** + * Translates the OS name to folder name. + * + * @param osName the OS name. + * @return the folder name. + */ + static String translateOSNameToFolderName(String osName) { + if (osName.contains("Windows")) { + return "Windows"; + } else if (osName.contains("Mac")) { + return "Mac"; + } else if (osName.contains("Linux")) { + return "Linux"; + } else if (osName.contains("AIX")) { + return "AIX"; + } + + else { + return osName.replaceAll("\\W", ""); + } + } + + /** + * Translates the architecture name to folder name. + * + * @param archName the architecture name. + * @return the folder name. + */ + static String translateArchNameToFolderName(String archName) { + return archName.replaceAll("\\W", ""); + } +} http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/main/java/org/apache/commons/crypto/utils/ReflectionUtils.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/crypto/utils/ReflectionUtils.java b/src/main/java/org/apache/commons/crypto/utils/ReflectionUtils.java new file mode 100644 index 0000000..9ffd7e8 --- /dev/null +++ b/src/main/java/org/apache/commons/crypto/utils/ReflectionUtils.java @@ -0,0 +1,204 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.crypto.utils; + +import java.lang.ref.WeakReference; +import java.lang.reflect.Constructor; +import java.util.Collections; +import java.util.Map; +import java.util.WeakHashMap; + +import org.apache.commons.crypto.cipher.Cipher; + +/** + * General utility methods for working with reflection. + */ +public class ReflectionUtils { + + private static final Map<ClassLoader, Map<String, WeakReference<Class<?>>>> + CACHE_CLASSES = new WeakHashMap<ClassLoader, Map<String, WeakReference<Class<?>>>>(); + + private static ClassLoader classLoader; + static { + classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader == null) { + classLoader = Cipher.class.getClassLoader(); + } + } + + /** + * Sentinel value to store negative cache results in {@link #CACHE_CLASSES}. + */ + private static final Class<?> NEGATIVE_CACHE_SENTINEL = + NegativeCacheSentinel.class; + + /** + * A unique class which is used as a sentinel value in the caching + * for getClassByName. {@link Cipher#getClassByNameOrNull(String)}. + */ + private static abstract class NegativeCacheSentinel {} + + /** + * Uses the constructor represented by this {@code Constructor} object to + * create and initialize a new instance of the constructor's + * declaring class, with the specified initialization parameters. + * + * @param klass the Class object. + * @param args array of objects to be passed as arguments to + * the constructor call. + * @return a new object created by calling the constructor + * this object represents. + */ + @SuppressWarnings("rawtypes") + public static <T> T newInstance(Class<T> klass, Object ... args) { + try { + Constructor<T> ctor = null; + + if (args.length == 0) { + ctor = klass.getDeclaredConstructor(new Class[] {}); + } else { + Class[] argClses = new Class[args.length]; + for (int i = 0; i < args.length; i++) { + argClses[i] = args[i].getClass(); + } + ctor = klass.getDeclaredConstructor(argClses); + } + ctor.setAccessible(true); + return ctor.newInstance(args); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Gets the value of the <code>name</code> property as a <code>Class</code> + * implementing the interface specified by <code>xface</code>. + * If no such property is specified, then <code>defaultValue</code> is + * returned.An exception is thrown if the returned class does not + * implement the named interface. + * + * @param name the class name of default implementation. + * @param defaultValue default value. + * @param xface the interface implemented by the named class. + * @return property value as a <code>Class</code>, + * or <code>defaultValue</code>. + */ + public static <U> Class<? extends U> getClass(String name, + Class<? extends U> defaultValue, + Class<U> xface) { + try { + Class<?> theClass = null; + if (name != null && !name.isEmpty()) { + theClass = getClassByName(name); + } + if (theClass == null) { + theClass = defaultValue; + } + if (theClass != null && !xface.isAssignableFrom(theClass)) + throw new RuntimeException(theClass+" not "+xface.getName()); + else if (theClass != null) + return theClass.asSubclass(xface); + else + return null; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Gets the value of the <code>name</code> property as a <code>Class</code>. + * If no such property is specified, then <code>defaultValue</code> is + * returned. + * + * @param name the class name. + * @param defaultValue default value. + * @return property value as a <code>Class</code>, + * or <code>defaultValue</code>. + */ + public static Class<?> getClass(String name, Class<?> defaultValue) { + String valueString = System.getProperty(name); + if (valueString == null) + return defaultValue; + try { + return getClassByName(valueString); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + /** + * Loads a class by name. + * + * @param name the class name. + * @return the class object. + * @throws ClassNotFoundException if the class is not found. + */ + public static Class<?> getClassByName(String name) + throws ClassNotFoundException { + Class<?> ret = getClassByNameOrNull(name); + if (ret == null) { + throw new ClassNotFoundException("Class " + name + " not found"); + } + return ret; + } + + /** + * Loads a class by name, returning null rather than throwing an exception + * if it couldn't be loaded. This is to avoid the overhead of creating + * an exception. + * + * @param name the class name. + * @return the class object, or null if it could not be found. + */ + private static Class<?> getClassByNameOrNull(String name) { + Map<String, WeakReference<Class<?>>> map; + + synchronized (CACHE_CLASSES) { + map = CACHE_CLASSES.get(classLoader); + if (map == null) { + map = Collections.synchronizedMap( + new WeakHashMap<String, WeakReference<Class<?>>>()); + CACHE_CLASSES.put(classLoader, map); + } + } + + Class<?> clazz = null; + WeakReference<Class<?>> ref = map.get(name); + if (ref != null) { + clazz = ref.get(); + } + + if (clazz == null) { + try { + clazz = Class.forName(name, true, classLoader); + } catch (ClassNotFoundException e) { + // Leave a marker that the class isn't found + map.put(name, new WeakReference<Class<?>>(NEGATIVE_CACHE_SENTINEL)); + return null; + } + // two putters can race here, but they'll put the same class + map.put(name, new WeakReference<Class<?>>(clazz)); + return clazz; + } else if (clazz == NEGATIVE_CACHE_SENTINEL) { + return null; // not found + } else { + // cache hit + return clazz; + } + } +} http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/main/java/org/apache/commons/crypto/utils/Utils.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/crypto/utils/Utils.java b/src/main/java/org/apache/commons/crypto/utils/Utils.java new file mode 100644 index 0000000..3658adf --- /dev/null +++ b/src/main/java/org/apache/commons/crypto/utils/Utils.java @@ -0,0 +1,362 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.crypto.utils; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.security.GeneralSecurityException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.Properties; + +import org.apache.commons.crypto.cipher.Cipher; +import org.apache.commons.crypto.cipher.CipherFactory; +import org.apache.commons.crypto.cipher.CipherTransformation; + +import static org.apache.commons.crypto.conf.ConfigurationKeys.CHIMERA_CRYPTO_STREAM_BUFFER_SIZE_DEFAULT; +import static org.apache.commons.crypto.conf.ConfigurationKeys.CHIMERA_CRYPTO_STREAM_BUFFER_SIZE_KEY; +import static org.apache.commons.crypto.conf.ConfigurationKeys.CHIMERA_CRYPTO_CIPHER_CLASSES_DEFAULT; +import static org.apache.commons.crypto.conf.ConfigurationKeys.CHIMERA_CRYPTO_CIPHER_CLASSES_KEY; +import static org.apache.commons.crypto.conf.ConfigurationKeys.CHIMERA_CRYPTO_CIPHER_JCE_PROVIDER_KEY; +import static org.apache.commons.crypto.conf.ConfigurationKeys.CHIMERA_CRYPTO_LIB_NAME_KEY; +import static org.apache.commons.crypto.conf.ConfigurationKeys.CHIMERA_CRYPTO_LIB_PATH_KEY; +import static org.apache.commons.crypto.conf.ConfigurationKeys.CHIMERA_CRYPTO_SECURE_RANDOM_DEVICE_FILE_PATH_DEFAULT; +import static org.apache.commons.crypto.conf.ConfigurationKeys.CHIMERA_CRYPTO_SECURE_RANDOM_DEVICE_FILE_PATH_KEY; +import static org.apache.commons.crypto.conf.ConfigurationKeys.CHIMERA_SYSTEM_PROPERTIES_FILE; +import static org.apache.commons.crypto.conf.ConfigurationKeys.CHIMERA_CRYPTO_LIB_TEMPDIR_KEY; + +/** + * General utility methods. + */ +public class Utils { + private static final int MIN_BUFFER_SIZE = 512; + + protected static final CipherTransformation AES_CTR_NOPADDING = CipherTransformation.AES_CTR_NOPADDING; + + /** + * For AES, the algorithm block is fixed size of 128 bits. + * @see http://en.wikipedia.org/wiki/Advanced_Encryption_Standard + */ + private static final int AES_BLOCK_SIZE = AES_CTR_NOPADDING.getAlgorithmBlockSize(); + + static { + loadChimeraSystemProperties(); + } + + /** + * loads system properties when configuration file of the name + * {@link #CHIMERA_SYSTEM_PROPERTIES_FILE} is found. + */ + private static void loadChimeraSystemProperties() { + try { + InputStream is = Thread.currentThread().getContextClassLoader() + .getResourceAsStream(CHIMERA_SYSTEM_PROPERTIES_FILE); + + if (is == null) + return; // no configuration file is found + + // Load property file + Properties props = new Properties(); + props.load(is); + is.close(); + Enumeration<?> names = props.propertyNames(); + while (names.hasMoreElements()) { + String name = (String) names.nextElement(); + if (name.startsWith("chimera.")) { + if (System.getProperty(name) == null) { + System.setProperty(name, props.getProperty(name)); + } + } + } + } catch (Throwable ex) { + System.err.println("Could not load '" + + CHIMERA_SYSTEM_PROPERTIES_FILE + "' from classpath: " + + ex.toString()); + } + } + + /** + * Forcibly free the direct buffer. + * + * @param buffer the bytebuffer to be freed. + */ + public static void freeDirectBuffer(ByteBuffer buffer) { + if (buffer instanceof sun.nio.ch.DirectBuffer) { + final sun.misc.Cleaner bufferCleaner = + ((sun.nio.ch.DirectBuffer) buffer).cleaner(); + bufferCleaner.clean(); + } + } + + /** + * Reads crypto buffer size. + * + * @param props The <code>Properties</code> class represents a set of + * properties. + * @return the buffer size. + * */ + public static int getBufferSize(Properties props) { + String bufferSizeStr = props.getProperty( + CHIMERA_CRYPTO_STREAM_BUFFER_SIZE_KEY); + if (bufferSizeStr == null || bufferSizeStr.isEmpty()) { + bufferSizeStr = System + .getProperty(CHIMERA_CRYPTO_STREAM_BUFFER_SIZE_KEY); + } + if (bufferSizeStr == null || bufferSizeStr.isEmpty()) { + return CHIMERA_CRYPTO_STREAM_BUFFER_SIZE_DEFAULT; + } else { + return Integer.parseInt(bufferSizeStr); + } + } + + /** + * Gets the cipher class. + * + * @param props The <code>Properties</code> class represents a set of + * properties. + * @return the cipher class based on the props. + */ + public static String getCipherClassString(Properties props) { + final String configName = CHIMERA_CRYPTO_CIPHER_CLASSES_KEY; + return props.getProperty(configName) != null ? props.getProperty(configName) : System + .getProperty(configName, CHIMERA_CRYPTO_CIPHER_CLASSES_DEFAULT); + } + + /** + * Gets the Jce provider. + * + * @param props The <code>Properties</code> class represents a set of + * properties. + * @return the jce provider based on the props. + */ + public static String getJCEProvider(Properties props) { + return props.getProperty(CHIMERA_CRYPTO_CIPHER_JCE_PROVIDER_KEY) != null ? + props.getProperty(CHIMERA_CRYPTO_CIPHER_JCE_PROVIDER_KEY) : + System.getProperty(CHIMERA_CRYPTO_CIPHER_JCE_PROVIDER_KEY); + } + + /** + * Gets the random device path. + * + * @param props The <code>Properties</code> class represents a set of + * properties. + * @return the random device path based on the props. + */ + public static String getRandomDevPath(Properties props) { + String devPath = props.getProperty( + CHIMERA_CRYPTO_SECURE_RANDOM_DEVICE_FILE_PATH_KEY); + if (devPath == null) { + devPath = System.getProperty( + CHIMERA_CRYPTO_SECURE_RANDOM_DEVICE_FILE_PATH_KEY, + CHIMERA_CRYPTO_SECURE_RANDOM_DEVICE_FILE_PATH_DEFAULT); + } + return devPath; + } + + /** + * Gets path of native library. + * + * @return the path of native library. + */ + public static String getLibPath() { + return System.getProperty(CHIMERA_CRYPTO_LIB_PATH_KEY); + } + + /** + * Gets the file name of native library. + * + * @return the file name of native library. + */ + public static String getLibName() { + return System.getProperty(CHIMERA_CRYPTO_LIB_NAME_KEY); + } + + /** + * Gets the temp directory for extracting crypto library. + * + * @return the temp directory. + */ + public static String getTmpDir() { + return System.getProperty(CHIMERA_CRYPTO_LIB_TEMPDIR_KEY, + System.getProperty("java.io.tmpdir")); + } + + /** + * Checks whether the cipher is supported streaming. + * + * @param cipher the {@link org.apache.commons.crypto.cipher.Cipher} instance. + * @throws IOException if an I/O error occurs. + */ + public static void checkStreamCipher(Cipher cipher) throws IOException { + if (cipher.getTransformation() != CipherTransformation.AES_CTR_NOPADDING) { + throw new IOException("AES/CTR/NoPadding is required"); + } + } + + /** + * Checks and floors buffer size. + * + * @param cipher the {@link org.apache.commons.crypto.cipher.Cipher} instance. + * @param bufferSize the buffer size. + * @return the remaining buffer size. + */ + public static int checkBufferSize(Cipher cipher, int bufferSize) { + checkArgument(bufferSize >= MIN_BUFFER_SIZE, + "Minimum value of buffer size is " + MIN_BUFFER_SIZE + "."); + return bufferSize - bufferSize % cipher.getTransformation() + .getAlgorithmBlockSize(); + } + + /** + * This method is only for Counter (CTR) mode. Generally the Cipher calculates the + * IV and maintain encryption context internally.For example a + * {@link javax.crypto.Cipher} will maintain its encryption context internally + * when we do encryption/decryption using the Cipher#update interface. + * <p/> + * Encryption/Decryption is not always on the entire file. For example, + * in Hadoop, a node may only decrypt a portion of a file (i.e. a split). + * In these situations, the counter is derived from the file position. + * <p/> + * The IV can be calculated by combining the initial IV and the counter with + * a lossless operation (concatenation, addition, or XOR). + * @see http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29 + * + * @param initIV initial IV + * @param counter counter for input stream position + * @param IV the IV for input stream position + */ + public static void calculateIV(byte[] initIV, long counter, byte[] IV) { + checkArgument(initIV.length == AES_BLOCK_SIZE); + checkArgument(IV.length == AES_BLOCK_SIZE); + + int i = IV.length; // IV length + int j = 0; // counter bytes index + int sum = 0; + while (i-- > 0) { + // (sum >>> Byte.SIZE) is the carry for addition + sum = (initIV[i] & 0xff) + (sum >>> Byte.SIZE); + if (j++ < 8) { // Big-endian, and long is 8 bytes length + sum += (byte) counter & 0xff; + counter >>>= 8; + } + IV[i] = (byte) sum; + } + } + + /** + * Helper method to create a Cipher instance and throws only IOException. + * + * @param props The <code>Properties</code> class represents a set of + * properties. + * @param transformation the CipherTransformation instance. + * @return the Cipher instance. + * @throws IOException if an I/O error occurs. + */ + public static Cipher getCipherInstance(CipherTransformation transformation, + Properties props) throws IOException { + try { + return CipherFactory.getInstance(transformation, props); + } catch (GeneralSecurityException e) { + throw new IOException(e); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to + * the calling method. + * + * @param expression a boolean expression. + * @throws IllegalArgumentException if expression is false. + */ + public static void checkArgument(boolean expression) { + if(!expression) { + throw new IllegalArgumentException(); + } + } + + /** + * Checks the truth of an expression. + * + * @param expression a boolean expression. + * @param errorMessage the exception message to use if the check fails; + * will be converted to a string using <code>String + * .valueOf(Object)</code>. + * @throws IllegalArgumentException if expression is false. + */ + public static void checkArgument(boolean expression, Object errorMessage) { + if (!expression) { + throw new IllegalArgumentException(String.valueOf(errorMessage)); + } + } + + /** + * Ensures that an object reference passed as a parameter to the calling + * method is not null. + * + * @param reference an object reference. + * @return the non-null reference that was validated. + * @throws NullPointerException if reference is null. + */ + public static <T> T checkNotNull(T reference) { + if(reference == null) { + throw new NullPointerException(); + } else { + return reference; + } + } + + /** + * Ensures the truth of an expression involving the state of the calling + * instance, but not involving any parameters to the calling method. + * + * @param expression a boolean expression. + * @throws IllegalStateException if expression is false. + */ + public static void checkState(boolean expression) { + if(!expression) { + throw new IllegalStateException(); + } + } + + /** + * Splits class names sequence into substrings, Trim each substring into an + * entry,and returns an list of the entries. + * + * @param clazzNames a string consist of a list of the entries joined by a + * delimiter. + * @param separator a delimiter for the input string. + * @return a list of class entries. + */ + public static List<String> splitClassNames(String clazzNames, + String separator) { + List<String> res = new ArrayList<String>(); + if (clazzNames == null || clazzNames.isEmpty()) { + return res; + } + + for (String clazzName : clazzNames.split(separator)) { + clazzName = clazzName.trim(); + if (!clazzName.isEmpty()) { + res.add(clazzName); + } + } + return res; + } +} http://git-wip-us.apache.org/repos/asf/commons-crypto/blob/4920d272/src/main/java/org/apache/commons/crypto/utils/package-info.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/crypto/utils/package-info.java b/src/main/java/org/apache/commons/crypto/utils/package-info.java new file mode 100644 index 0000000..7a3c334 --- /dev/null +++ b/src/main/java/org/apache/commons/crypto/utils/package-info.java @@ -0,0 +1,21 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Utils classes + */ +package org.apache.commons.crypto.utils;