com.microsoft.azure.storage.blob.BlobDecryptStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of azure-storage Show documentation
Show all versions of azure-storage Show documentation
SDK for Microsoft Azure Storage Clients
/**
* Copyright Microsoft Corporation
*
* Licensed 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 com.microsoft.azure.storage.blob;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
import com.microsoft.azure.storage.StorageException;
import com.microsoft.azure.storage.core.Utility;
/**
* Stream that will be used for decrypting blob ranges. It buffers 16 bytes of IV (if required) before creating a crypto
* stream and routing the rest of the data through it.
*/
class BlobDecryptStream extends BlobOutputStream {
private final OutputStream userStream;
private final Map metadata;
private long position;
private Long userProvidedLength;
private byte[] iv = new byte[16];
private BlobEncryptionPolicy encryptionPolicy;
private int discardFirst;
private OutputStream cryptoStream;
private boolean bufferIV;
private boolean noPadding;
private Boolean requireEncryption;
public BlobDecryptStream(OutputStream userStream, Map metadata, Long userProvidedLength,
int discardFirst, boolean bufferIV, boolean noPadding, BlobEncryptionPolicy policy,
final Boolean requireEncryption)
{
this.userStream = userStream;
this.metadata = metadata;
this.userProvidedLength = userProvidedLength;
this.discardFirst = discardFirst;
this.encryptionPolicy = policy;
this.bufferIV = bufferIV;
this.noPadding = noPadding;
this.requireEncryption = requireEncryption;
}
@Override
public void close() throws IOException
{
this.cryptoStream.close();
}
@Override
void abort() throws IOException {
// no-op. This method is used in the case of aborting uploads, and decrypt streams are on downloads.
}
@Override
public void write(byte[] data, int offset, int length) throws IOException {
// Keep buffering until we have 16 bytes of IV.
if (this.bufferIV && this.position < 16)
{
int bytesToCopy = 16 - (int)this.position;
bytesToCopy = length > bytesToCopy ? bytesToCopy : length;
System.arraycopy(data, offset, this.iv, (int)this.position, bytesToCopy);
this.position += bytesToCopy;
offset += bytesToCopy;
length -= bytesToCopy;
}
// Wrap user stream with LengthLimitingStream. This stream will be used to discard the extra bytes we downloaded in order to deal with AES block size.
// Create crypto stream around the length limiting stream once per download and start writing to it. During retries, the state is maintained and
// new crypto streams will not be created each time.
if (this.cryptoStream == null)
{
LengthLimitingStream lengthLimitingStream = new LengthLimitingStream(this.userStream, this.discardFirst,
this.userProvidedLength);
try {
this.cryptoStream = this.encryptionPolicy.decryptBlob(lengthLimitingStream, this.metadata,
this.requireEncryption, !this.bufferIV ? null : this.iv, this.noPadding);
}
catch (StorageException e) {
throw Utility.initIOException(e);
}
}
// Route the remaining data through the crypto stream.
if (length > 0)
{
this.cryptoStream.write(data, offset, length);
this.position += length;
}
}
@Override
public void write(InputStream sourceStream, long writeLength) throws IOException, StorageException {
Utility.writeToOutputStream(sourceStream, this, writeLength, false, false, null, null);
}
@Override
public void flush() throws IOException {
this.userStream.flush();
}
}