001: /*
002: * $Id: AnyCipher.java,v 1.19 2002/09/16 08:05:02 jkl Exp $
003: *
004: * Copyright (c) 2002 Njet Communications Ltd. All Rights Reserved.
005: *
006: * Use is subject to license terms, as defined in
007: * Anvil Sofware License, Version 1.1. See LICENSE
008: * file, or http://njet.org/license-1.1.txt
009: */
010: package anvil.core.crypto;
011:
012: import anvil.core.Any;
013: import anvil.core.AnyBinary;
014: import anvil.core.AnyAbstractClass;
015: import anvil.script.Context;
016: import java.io.IOException;
017: import java.security.InvalidAlgorithmParameterException;
018: import java.security.InvalidKeyException;
019: import java.security.NoSuchAlgorithmException;
020: import javax.crypto.Cipher;
021: import javax.crypto.SecretKey;
022: import javax.crypto.NoSuchPaddingException;
023: import javax.crypto.spec.DESedeKeySpec;
024: import javax.crypto.spec.DESKeySpec;
025: import javax.crypto.spec.IvParameterSpec;
026: import javax.crypto.spec.SecretKeySpec;
027:
028: ///
029: /// @class Cipher
030: /// Used for encrypting and decrypting data.
031: /// Using symmetric algorithms: <i>DES, TripleDES, Blowfish</i>.
032: ///
033:
034: /**
035: * class AnyCipher
036: *
037: * @author: Jaripekka Salminen
038: */
039: public class AnyCipher extends AnyAbstractClass {
040: public static final anvil.script.compiler.NativeClass __class__ = new anvil.script.compiler.NativeClass(
041: "Cipher",
042: AnyCipher.class,
043: //DOC{{
044: ""
045: + "\n"
046: + " @class Cipher\n"
047: + " Used for encrypting and decrypting data.\n"
048: + " Using symmetric algorithms: <i>DES, TripleDES, Blowfish</i>.\n"
049: + "\n"
050: + " @method update\n"
051: + " Continues a multiple-part encryption or decryption\n"
052: + " operation (depending on how this cipher was initialized),\n"
053: + " processing another data part. \n"
054: + " @synopsis binary update(object data [, int offset, int length] )\n"
055: + " @param data string or binary data\n"
056: + " @param offset offset in data\n"
057: + " @param length length of data\n"
058: + " @return processed data\n"
059: + " @method final\n"
060: + " @synopsis binary final() ;\n"
061: + " Finishes a multiple-part encryption or decryption\n"
062: + " operation, depending on how this cipher was initialized.\n"
063: + " @synopsis binary final(object data [, int offset, int length] ) ;\n"
064: + " Encrypts or decrypts data in a single-part operation, or\n"
065: + " finishes a multiple-part operation.\n"
066: + " @param data string or binary data\n"
067: + " @param offset offset in data\n"
068: + " @param length length of data\n"
069: + " @return processed data\n"
070: //}}DOC
071: );
072: static {
073: CryptoModule.class.getName();
074: }
075:
076: private Cipher _cipher;
077:
078: /**
079: * @param algorithm = DES | DESede | Blowfish
080: * @param opmode = Cipher.ENCRYPT_MODE | Ciper.DECRYPT_MODE
081: */
082: public AnyCipher(String algorithm, int opmode, Any anyKey)
083: throws NoSuchAlgorithmException, NoSuchPaddingException,
084: InvalidKeyException, InvalidAlgorithmParameterException,
085: IOException {
086: String mode = "CBC"; // ECB | CBC | CFB | OFB | PCPC
087: String padding = "PKCS5Padding"; // NoPadding | PKCS5Padding | SSL3Padding
088: StringBuffer transformation = new StringBuffer(algorithm);
089: transformation.append('/');
090: transformation.append(mode);
091: transformation.append('/');
092: transformation.append(padding);
093: _cipher = Cipher.getInstance(transformation.toString());
094:
095: byte[] key;
096: int key_length;
097: if (anyKey.isBinary()) {
098: key = (byte[]) anyKey.toBinary();
099: key_length = anyKey.sizeOf();
100: } else {
101: key = anvil.util.Conversions.getBytes(anyKey.toString());
102: key_length = key.length;
103: }
104:
105: if (algorithm.equals("DES")) {
106: byte[] deskey = new byte[8];
107: for (int i = 0; i < 8; i++) {
108: if (i < key_length) {
109: deskey[i] = key[i];
110: } else {
111: deskey[i] = 0;
112: }
113: }
114: key = (new DESKeySpec(deskey)).getKey();
115:
116: } else if (algorithm.equals("DESede")) {
117: byte[] desedekey = new byte[24];
118: for (int i = 0; i < 24; i++) {
119: if (i < key_length) {
120: desedekey[i] = key[i];
121: } else {
122: desedekey[i] = 0;
123: }
124: }
125: key = (new DESedeKeySpec(desedekey)).getKey();
126: }
127:
128: SecretKey secretKey = new SecretKeySpec(key, algorithm);
129:
130: /* DES in ECB mode does not require any parameters */
131: if (mode.equals("ECB")) {
132: _cipher.init(opmode, secretKey);
133:
134: /* DES in CBC mode requires an initialization vector (IV) parameter */
135: } else if (mode.equals("CBC")) {
136: byte[] iv = new byte[8];
137: IvParameterSpec ivSpec = new IvParameterSpec(iv);
138: _cipher.init(opmode, secretKey, ivSpec);
139:
140: } else {
141: _cipher.init(opmode, secretKey);
142: }
143: }
144:
145: /**
146: *
147: */
148:
149: public final anvil.script.ClassType classOf() {
150: return __class__;
151: }
152:
153: /**
154: *
155: */
156: public Object toObject() {
157: return _cipher;
158: }
159:
160: /**
161: *
162: */
163:
164: /// @method update
165: /// Continues a multiple-part encryption or decryption
166: /// operation (depending on how this cipher was initialized),
167: /// processing another data part.
168: /// @synopsis binary update(object data [, int offset, int length] )
169: /// @param data string or binary data
170: /// @param offset offset in data
171: /// @param length length of data
172: /// @return processed data
173: public Any m_update(Context context, Any[] parameters) {
174: if (parameters.length > 0) {
175: try {
176: Any param = parameters[0];
177: byte[] data;
178: int size;
179: if (param.isBinary()) {
180: data = param.toBinary();
181: size = param.sizeOf();
182: } else {
183: data = anvil.util.Conversions.getBytes(param
184: .toString());
185: size = data.length;
186: }
187: if (parameters.length > 2) {
188: int offset = parameters[1].toInt();
189: int length = parameters[2].toInt();
190: if (offset < 0) {
191: offset = 0;
192: }
193: if (offset >= size) {
194: new AnyBinary();
195: }
196: if (offset + length > size) {
197: length = size - offset;
198: }
199: return Any.create(_cipher.update(data, offset,
200: length));
201:
202: } else {
203: return Any.create(_cipher.update(data, 0, size));
204: }
205: } catch (Exception e) {
206: throw context.exception(e);
207: }
208: } else {
209: throw parametersMissing(context, "final");
210: }
211: }
212:
213: /// @method final
214: /// @synopsis binary final() ;
215: /// Finishes a multiple-part encryption or decryption
216: /// operation, depending on how this cipher was initialized.
217: /// @synopsis binary final(object data [, int offset, int length] ) ;
218: /// Encrypts or decrypts data in a single-part operation, or
219: /// finishes a multiple-part operation.
220: /// @param data string or binary data
221: /// @param offset offset in data
222: /// @param length length of data
223: /// @return processed data
224: public Any m_final(Context context, Any[] parameters) {
225: try {
226: if (parameters.length > 0) {
227: Any param = parameters[0];
228: byte[] data;
229: int size;
230: if (param.isBinary()) {
231: data = param.toBinary();
232: size = param.sizeOf();
233: } else {
234: data = anvil.util.Conversions.getBytes(param
235: .toString());
236: size = data.length;
237: }
238: if (parameters.length > 2) {
239: int offset = parameters[1].toInt();
240: int length = parameters[2].toInt();
241: if (offset < 0) {
242: offset = 0;
243: }
244: if (offset >= size) {
245: return Any.create(_cipher.doFinal());
246: }
247: if (offset + length > size) {
248: length = size - offset;
249: }
250: return Any.create(_cipher.doFinal(data, offset,
251: length));
252:
253: } else {
254: return Any.create(_cipher.doFinal(data, 0, size));
255: }
256: } else {
257: return Any.create(_cipher.doFinal());
258: }
259: } catch (Exception e) {
260: throw context.exception(e);
261: }
262: }
263:
264: }
|