/*
* $Id: AnyCipher.java,v 1.19 2002/09/16 08:05:02 jkl Exp $
*
* Copyright (c) 2002 Njet Communications Ltd. All Rights Reserved.
*
* Use is subject to license terms, as defined in
* Anvil Sofware License, Version 1.1. See LICENSE
* file, or http://njet.org/license-1.1.txt
*/
package anvil.core.crypto;
import anvil.core.Any;
import anvil.core.AnyBinary;
import anvil.core.AnyAbstractClass;
import anvil.script.Context;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
///
/// @class Cipher
/// Used for encrypting and decrypting data.
/// Using symmetric algorithms: <i>DES, TripleDES, Blowfish</i>.
///
/**
* class AnyCipher
*
* @author: Jaripekka Salminen
*/
public class AnyCipher extends AnyAbstractClass
{
public static final anvil.script.compiler.NativeClass __class__ =
new anvil.script.compiler.NativeClass("Cipher", AnyCipher.class,
//DOC{{
""+
"\n" +
" @class Cipher\n" +
" Used for encrypting and decrypting data.\n" +
" Using symmetric algorithms: <i>DES, TripleDES, Blowfish</i>.\n" +
"\n" +
" @method update\n" +
" Continues a multiple-part encryption or decryption\n" +
" operation (depending on how this cipher was initialized),\n" +
" processing another data part. \n" +
" @synopsis binary update(object data [, int offset, int length] )\n" +
" @param data string or binary data\n" +
" @param offset offset in data\n" +
" @param length length of data\n" +
" @return processed data\n" +
" @method final\n" +
" @synopsis binary final() ;\n" +
" Finishes a multiple-part encryption or decryption\n" +
" operation, depending on how this cipher was initialized.\n" +
" @synopsis binary final(object data [, int offset, int length] ) ;\n" +
" Encrypts or decrypts data in a single-part operation, or\n" +
" finishes a multiple-part operation.\n" +
" @param data string or binary data\n" +
" @param offset offset in data\n" +
" @param length length of data\n" +
" @return processed data\n"
//}}DOC
);
static {
CryptoModule.class.getName();
}
private Cipher _cipher;
/**
* @param algorithm = DES | DESede | Blowfish
* @param opmode = Cipher.ENCRYPT_MODE | Ciper.DECRYPT_MODE
*/
public AnyCipher(String algorithm, int opmode, Any anyKey)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException, IOException
{
String mode = "CBC"; // ECB | CBC | CFB | OFB | PCPC
String padding = "PKCS5Padding"; // NoPadding | PKCS5Padding | SSL3Padding
StringBuffer transformation = new StringBuffer(algorithm);
transformation.append('/');
transformation.append(mode);
transformation.append('/');
transformation.append(padding);
_cipher = Cipher.getInstance(transformation.toString());
byte [] key;
int key_length;
if (anyKey.isBinary()) {
key = (byte[])anyKey.toBinary();
key_length = anyKey.sizeOf();
} else {
key = anvil.util.Conversions.getBytes(anyKey.toString());
key_length = key.length;
}
if (algorithm.equals("DES")) {
byte[] deskey = new byte[8];
for (int i=0; i<8; i++) {
if (i < key_length) {
deskey[i] = key[i];
} else {
deskey[i] = 0;
}
}
key = (new DESKeySpec(deskey)).getKey();
} else if (algorithm.equals("DESede")) {
byte[] desedekey = new byte[24];
for (int i=0; i<24; i++) {
if (i < key_length) {
desedekey[i] = key[i];
} else {
desedekey[i] = 0;
}
}
key = (new DESedeKeySpec(desedekey)).getKey();
}
SecretKey secretKey = new SecretKeySpec(key,algorithm);
/* DES in ECB mode does not require any parameters */
if (mode.equals("ECB")) {
_cipher.init(opmode, secretKey);
/* DES in CBC mode requires an initialization vector (IV) parameter */
} else if (mode.equals("CBC")) {
byte[] iv = new byte[8];
IvParameterSpec ivSpec = new IvParameterSpec(iv);
_cipher.init(opmode, secretKey, ivSpec);
} else {
_cipher.init(opmode, secretKey);
}
}
/**
*
*/
public final anvil.script.ClassType classOf() {
return __class__;
}
/**
*
*/
public Object toObject()
{
return _cipher;
}
/**
*
*/
/// @method update
/// Continues a multiple-part encryption or decryption
/// operation (depending on how this cipher was initialized),
/// processing another data part.
/// @synopsis binary update(object data [, int offset, int length] )
/// @param data string or binary data
/// @param offset offset in data
/// @param length length of data
/// @return processed data
public Any m_update(Context context, Any[] parameters)
{
if (parameters.length > 0) {
try {
Any param = parameters[0];
byte[] data;
int size;
if (param.isBinary()) {
data = param.toBinary();
size = param.sizeOf();
} else {
data = anvil.util.Conversions.getBytes(param.toString());
size = data.length;
}
if (parameters.length > 2) {
int offset = parameters[1].toInt();
int length = parameters[2].toInt();
if (offset < 0) {
offset = 0;
}
if (offset >= size) {
new AnyBinary();
}
if (offset + length > size) {
length = size - offset;
}
return Any.create(_cipher.update(data, offset, length));
} else {
return Any.create(_cipher.update(data, 0, size));
}
} catch (Exception e) {
throw context.exception(e);
}
} else {
throw parametersMissing(context, "final");
}
}
/// @method final
/// @synopsis binary final() ;
/// Finishes a multiple-part encryption or decryption
/// operation, depending on how this cipher was initialized.
/// @synopsis binary final(object data [, int offset, int length] ) ;
/// Encrypts or decrypts data in a single-part operation, or
/// finishes a multiple-part operation.
/// @param data string or binary data
/// @param offset offset in data
/// @param length length of data
/// @return processed data
public Any m_final(Context context, Any[] parameters)
{
try {
if (parameters.length > 0) {
Any param = parameters[0];
byte[] data;
int size;
if (param.isBinary()) {
data = param.toBinary();
size = param.sizeOf();
} else {
data = anvil.util.Conversions.getBytes(param.toString());
size = data.length;
}
if (parameters.length > 2) {
int offset = parameters[1].toInt();
int length = parameters[2].toInt();
if (offset < 0) {
offset = 0;
}
if (offset >= size) {
return Any.create(_cipher.doFinal());
}
if (offset + length > size) {
length = size - offset;
}
return Any.create(_cipher.doFinal(data, offset, length));
} else {
return Any.create(_cipher.doFinal(data, 0, size));
}
} else {
return Any.create(_cipher.doFinal());
}
} catch (Exception e) {
throw context.exception(e);
}
}
}