/*
* Copyright 2011 yingxinwu.g@gmail.com
*
* 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 xink.crypto;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import xink.vpn.Assert;
import xink.vpn.R;
import android.content.Context;
public final class StreamCrypto {
/*
* Buffer size when reading input.
*/
private static final int BUF_SIZE = 128;
private static byte[] rawKey;
/**
* Initialization.
*
* @param ctx
* Context
*/
public static void init(final Context ctx) {
rawKey = ctx.getString(R.string.crypto_raw_key).getBytes();
Assert.isEquals(rawKey.length, 16, "AES-128 requires a 16 bytes raw key");
}
/**
* Encrypt bytes from input stream, and writes to the output stream.<br>
* input and output stream will be closed before this method returns.
*
* @throws IOException
* when read/write file failed
* @throws GeneralSecurityException
* when encrypt error occurs
*/
public static void encrypt(final InputStream in, final OutputStream out) throws IOException, GeneralSecurityException {
process(Cipher.ENCRYPT_MODE, in, out);
}
/**
* Decrypt bytes from input stream, and writes to the output stream.<br>
* input and output stream will be closed before this method returns.
*
* @throws IOException
* when read/write file failed
* @throws GeneralSecurityException
* when decrypt error occurs
*/
public static void decrypt(final InputStream in, final OutputStream out) throws IOException, GeneralSecurityException {
process(Cipher.DECRYPT_MODE, in, out);
}
private static void process(final int mode, final InputStream in, final OutputStream out) throws IOException, GeneralSecurityException {
CipherOutputStream co = null;
try {
co = doProcess(mode, in, out);
} finally {
in.close();
if (co != null) {
co.close();
}
}
}
private static CipherOutputStream doProcess(final int mode, final InputStream in, final OutputStream out) throws IOException,
GeneralSecurityException {
Cipher c = getCipher(mode);
CipherOutputStream co = new CipherOutputStream(out, c);
byte[] buf = new byte[BUF_SIZE];
int len = -1;
while ((len = in.read(buf)) > 0) {
co.write(buf, 0, len);
}
return co;
}
private static Cipher getCipher(final int mode) throws GeneralSecurityException {
SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
IvParameterSpec ivps = new IvParameterSpec(rawKey);
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(mode, skeySpec, ivps);
return c;
}
private StreamCrypto() {
}
}