You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
yacy_search_server/source/de/anomic/tools/cryptbig.java

669 lines
27 KiB

// crypt.java
// -------------------------------------
// (C) by Michael Peter Christen; mc@anomic.de
// first published on http://www.anomic.de
// Frankfurt, Germany, 2004
// last major change: 13.05.2004
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Using this software in any meaning (reading, learning, copying, compiling,
// running) means that you agree that the Author(s) is (are) not responsible
// for cost, loss of data or any harm that may be caused directly or indirectly
// by usage of this softare or this documentation. The usage of this software
// is on your own risk. The installation and usage (starting/running) of this
// software may allow other people or application to access your computer and
// any attached devices and is highly dependent on the configuration of the
// software which must be done by the user of the software; the author(s) is
// (are) also not responsible for proper configuration and usage of the
// software, even if provoked by documentation provided together with
// the software.
//
// Any changes to this file according to the GPL as documented in the file
// gpl.txt aside this file in the shipment you received can be done to the
// lines that follows this copyright notice here, but changes must not be
// done inside the copyright notive above. A re-distribution must contain
// the intact and unchanged copyright notice.
// Contributions and changes to the program code must be marked as such.
package de.anomic.tools;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.security.Provider;
import java.security.Security;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.Random;
import java.util.Set;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import de.anomic.kelondro.kelondroBase64Order;
import de.anomic.server.serverCodings;
public class cryptbig {
// --------------------------------------------------------
// Section: random salt generation
// --------------------------------------------------------
private static long saltcounter = 0;
private static Random saltrandom = new Random(System.currentTimeMillis());
public static String randomSalt() {
// generate robust 48-bit random number
long salt = (saltrandom.nextLong() & 0XffffffffffffL) + (System.currentTimeMillis() & 0XffffffffffffL) + ((1001 * saltcounter) & 0XffffffffffffL);
saltcounter++;
// we generate 48-bit salt values, that are represented as 8-character
// b64-encoded strings
return kelondroBase64Order.standardCoder.encodeLong(salt & 0XffffffffffffL, 8);
}
// --------------------------------------------------------
// Section: PBE + PublicKey based on passwords encryption
// --------------------------------------------------------
public static final String vDATE = "20030925";
public static final String copyright = "[ 'crypt' v" + vDATE + " by Michael Christen / www.anomic.de ]";
public static final String magicString = "crypt|anomic.de|0"; // magic identifier inside every '.crypt' - file
public static final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyyMMddHHmmssSSS", Locale.ENGLISH);
String cryptMethod; // one of ["TripleDES", "Blowfish", "DESede", "DES"]
private static final String defaultMethod = "PBEWithMD5AndDES"; //"DES";
Cipher ecipher;
Cipher dcipher;
public cryptbig(String pbe) {
// this is possible, but not recommended
this(pbe, (pbe + "XXXXXXXX").substring(0, 8));
}
public cryptbig(String pbe, String salt) {
this(pbe, salt, defaultMethod);
}
private cryptbig(String pbe, String salt, String method) {
// a Password-Based Encryption. The SecretKey is created on the fly
PBEKeySpec keySpec = new PBEKeySpec(pbe.toCharArray());
try {
if (salt.length() > 8) salt = salt.substring(0,8);
if (salt.length() < 8) salt = (salt + "XXXXXXXX").substring(0,8);
// create the PBE key
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(method);
SecretKey key = keyFactory.generateSecret(keySpec);
// create parameter spec for PBE
PBEParameterSpec paramSpec = new PBEParameterSpec(salt.getBytes(), 1000 /*ITERATIONS*/);
// Create a cipher and initialize it for encrypting end decrypting
cryptMethod = method;
ecipher = Cipher.getInstance(cryptMethod);
dcipher = Cipher.getInstance(cryptMethod);
ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec); // paramSpec only for PBE!
dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
} catch (javax.crypto.NoSuchPaddingException e) {
} catch (java.security.InvalidKeyException e) {
} catch (java.security.NoSuchAlgorithmException e) {
} catch (java.security.spec.InvalidKeySpecException e) {
} catch (java.security.InvalidAlgorithmParameterException e) {
}
}
// Encode a string into a new string using utf-8, crypt and b64
public String encryptString(String str) {
try {
byte[] utf = str.getBytes("UTF8");
byte[] enc = encryptArray(utf);
if (enc == null) return null;
return kelondroBase64Order.standardCoder.encode(enc);
} catch (UnsupportedEncodingException e) {
}
return null;
}
// Decode a string into a new string using b64, crypt and utf-8
public String decryptString(String str) {
try {
byte[] b64dec = kelondroBase64Order.standardCoder.decode(str);
if (b64dec == null) return null; // error in input string (inconsistency)
byte[] dec = decryptArray(b64dec);
if (dec == null) return null;
return new String(dec, "UTF8");
} catch (UnsupportedEncodingException e) {
}
return null;
}
// Encode a byte array into a new byte array
public byte[] encryptArray(byte[] b) {
if (b == null) return null;
try {
return ecipher.doFinal(b);
} catch (javax.crypto.BadPaddingException e) {
} catch (IllegalBlockSizeException e) {
}
return null;
}
// Decode a string into a new string using b64, crypt and utf-8
public byte[] decryptArray(byte[] b) {
if (b == null) return null;
try {
return dcipher.doFinal(b);
} catch (javax.crypto.BadPaddingException e) {
} catch (IllegalBlockSizeException e) {
}
return null;
}
// This method returns the available implementations for a service type
public static Set listCryptoMethods(String serviceType) {
Set result = new HashSet();
// All providers
Provider[] providers = Security.getProviders();
for (int i = 0; i < providers.length; i++) {
// Get services provided by each provider
Set keys = providers[i].keySet();
for (Iterator it = keys.iterator(); it.hasNext(); ) {
String key = (String) it.next();
key = key.split(" ")[0];
if (key.startsWith(serviceType + ".")) {
result.add(key.substring(serviceType.length() + 1));
} else if (key.startsWith("Alg.Alias." + serviceType + ".")) {
// This is an alias
result.add(key.substring(serviceType.length() + 11));
}
}
}
return result;
}
public static void testCryptMethods(Set methods) {
String method;
Iterator i = methods.iterator();
while (i.hasNext()) {
method = (String) i.next();
System.out.print(method + " : ");
try {
cryptbig crypter = new cryptbig("abrakadabra", method);
String encrypted = crypter.encryptString("nicht verraten abc 1234567890");
System.out.print(encrypted + "/");
String decrypted = crypter.decryptString(encrypted);
System.out.println(decrypted);
} catch (Exception e) {
System.out.println("Exception: " + e.getMessage());
e.printStackTrace();
}
}
}
public void encryptFile(String inFileName, String outFileName, boolean compress) {
/*
File-Format of encrypted file:
Filename: b64-of-encryption-of-<encryption-date: YYYYMMddHHmmSSsss> plus extension ".crypt"
File Content:
<original file name>
<original file date>
<original file size>
<compressed-before-encryption-flag>
<compressed-after-encryption-flag>
<binary>
*/
try {
File inFile = new File(inFileName);
String inFileDate = dateFormatter.format(new Date(inFile.lastModified())); // 17 byte
String encryptionDate = dateFormatter.format(new Date()); // 17 byte
String inFileSize = kelondroBase64Order.standardCoder.encodeLong(inFile.length(), 11); // 64 / 6 = 11; 11 byte
String flag = "1"; // 1 byte
//int inFileNameLength = inFileName.length(); // 256
String X = inFileDate + encryptionDate + inFileSize + flag + inFileName;
System.out.println("TEST: preserving inFileDate : " + dateFormatter.parse(inFileDate, new ParsePosition(0)));
System.out.println("TEST: preserving encryptionDate: " + dateFormatter.parse(encryptionDate, new ParsePosition(0)));
System.out.println("TEST: preserving inFileLength : " + inFile.length());
System.out.println("TEST: preserving flag : " + flag);
System.out.println("TEST: preserving inFileName : " + inFileName);
System.out.println("TEST: preserving X-String : " + X);
// start encryption
InputStream fin = new CipherInputStream(new FileInputStream(inFile), ecipher);
OutputStream fout = new FileOutputStream(outFileName);
// write magic and properties of original file
// - we encrypt the original date, the encryption date, the file size, the flag
// and file name together to the string A and calculate the length AL of that string
// - the length of the file name is therefore equal to AL-(17+17+11+1) = AL-46
// - AL is then b64-ed and also encrypted which results into string B
// - the length of B is BL; BL is then b64-ed to a string C of fixed length 1
// - after the magic String we write C, B and A
try {
String A = new String(ecipher.doFinal(X.getBytes("UTF8")));
String B = new String(ecipher.doFinal(kelondroBase64Order.standardCoder.encodeLong(A.length(), 2).getBytes("UTF8"))); // most probable not longer than 4
String C = kelondroBase64Order.standardCoder.encodeLong(B.length(), 1); // fixed length 1 (6 bits, that should be enough)
fout.write(magicString.getBytes()); // the magic string, used to identify a 'crypt'-file
fout.write(C.getBytes());
fout.write(B.getBytes());
fout.write(A.getBytes());
// write content of file
copy(fout, fin, 512);
}
catch (javax.crypto.IllegalBlockSizeException e) {System.err.println("ERROR:" + e.getMessage());}
catch (javax.crypto.BadPaddingException e) {System.err.println("ERROR:" + e.getMessage());}
// finished files
fin.close();
fout.close();
} catch (FileNotFoundException e) {
System.err.println("ERROR: file '" + inFileName + "' not found");
} catch (IOException e) {
System.err.println("ERROR: IO trouble");
}
}
public void decryptFile(String inFileName, String outFileName) {
InputStream fin = null;
OutputStream fout = null;
try {
// Start opening the files
fin = new BufferedInputStream(new FileInputStream(inFileName), 4096);
// read the file properties
byte[] thisMagic = new byte[magicString.length()]; fin.read(thisMagic);
if (!((new String(thisMagic)).equals(magicString))) {
// this is not an crypt file, so dont do anything
fin.close();
return;
}
byte[] C = new byte[1]; fin.read(C); // the length of the following String, encoded as b64
byte[] B = new byte[(int) kelondroBase64Order.standardCoder.decodeLong(new String(C))]; fin.read(B); // this is again the length of the following string, as encrypted b64-ed integer
byte[] A = new byte[(int) kelondroBase64Order.standardCoder.decodeLong(new String(dcipher.doFinal(B), "UTF8"))]; fin.read(A);
String X = new String(dcipher.doFinal(A), "UTF8");
System.out.println("TEST: detecting X-String : " + X);
// reconstruct the properties
Date inFileDate = dateFormatter.parse(X.substring(0, 17), new ParsePosition(0));
Date encryptionDate = dateFormatter.parse(X.substring(17, 34), new ParsePosition(0));
long inFileSize = kelondroBase64Order.standardCoder.decodeLong(X.substring(34, 45));
String flag = X.substring(45, 46);
String origFileName = X.substring(46);
System.out.println("TEST: detecting inFileDate : " + inFileDate);
System.out.println("TEST: detecting encryptionDate: " + encryptionDate);
System.out.println("TEST: detecting inFileLength : " + inFileSize);
System.out.println("TEST: detecting flag : " + flag);
System.out.println("TEST: detecting inFileName : " + origFileName);
// open the output file
fout = new BufferedOutputStream(new CipherOutputStream(new FileOutputStream(outFileName), dcipher), 4096);
// read and decrypt the file
copy(fout, fin, 512);
// close the files
fin.close();
fout.close();
// do postprocessing
} catch (BadPaddingException e) {
System.err.println("ERROR: decryption of '" + inFileName + "' not possible: " + e.getMessage());
} catch (IllegalBlockSizeException e) {
System.err.println("ERROR: decryption of '" + inFileName + "' not possible: " + e.getMessage());
} catch (FileNotFoundException e) {
System.err.println("ERROR: file '" + inFileName + "' not found");
} catch (IOException e) {
System.err.println("ERROR: IO trouble");
try {fin.close(); fout.close();} catch (Exception ee) {}
}
}
private static void copy(OutputStream out, InputStream in, int bufferSize) throws IOException {
InputStream bIn = new BufferedInputStream(in, bufferSize);
OutputStream bOut = new BufferedOutputStream(out, bufferSize);
byte[] buf = new byte[bufferSize];
int n;
while ((n = bIn.read(buf)) > 0) bOut.write(buf, 0, n);
bIn.close();
bOut.close();
}
public static String scrambleString(String key, String s) {
// we perform several operations
// - generate salt
// - gzip string
// - crypt string with key and salt
// - base64-encode result
// - attach salt and return
String salt = randomSalt();
//System.out.println("Salt=" + salt);
cryptbig c = new cryptbig(key, salt);
boolean gzFlag = true;
byte[] gz = gzip.gzipString(s);
if (gz.length > s.length()) {
// revert compression
try {
gz = s.getBytes("UTF8");
gzFlag = false;
} catch (UnsupportedEncodingException e) {
return null;
}
}
//System.out.println("GZIP length=" + gz.length);
if (gz == null) return null;
byte[] enc = c.encryptArray(gz);
if (enc == null) return null;
return salt + ((gzFlag) ? "1" : "0") + kelondroBase64Order.enhancedCoder.encode(enc);
}
public static String descrambleString(String key, String s) {
String salt = s.substring(0, 8);
boolean gzFlag = (s.charAt(8) == '1');
s = s.substring(9);
cryptbig c = new cryptbig(key, salt);
byte[] b64dec = kelondroBase64Order.enhancedCoder.decode(s);
if (b64dec == null) return null; // error in input string (inconsistency)
byte[] dec = c.decryptArray(b64dec);
if (dec == null) return null;
if (gzFlag)
return gzip.gunzipString(dec);
else
try {return new String(dec,"UTF8");} catch (UnsupportedEncodingException e) {return null;}
}
// --------------------------------------------------------
// Section: simple Codings
// --------------------------------------------------------
public static String simpleEncode(String content) {
return simpleEncode(content, null, 'b');
}
public static String simpleEncode(String content, String key) {
return simpleEncode(content, key, 'b');
}
public static String simpleEncode(String content, String key, char method) {
if (key == null) key = "NULL";
if (method == 'p') return "p|" + content;
if (method == 'b') return "b|" + kelondroBase64Order.enhancedCoder.encodeString(content);
if (method == 'z') return "z|" + kelondroBase64Order.enhancedCoder.encode(gzip.gzipString(content));
if (method == 'c') return "c|" + scrambleString(key, content);
return null;
}
public static String simpleDecode(String encoded, String key) {
if ((encoded == null) || (encoded.length() < 3)) return null;
if (encoded.charAt(1) != '|') return encoded; // not encoded
char method = encoded.charAt(0);
encoded = encoded.substring(2);
if (method == 'p') return encoded;
if (method == 'b') return kelondroBase64Order.enhancedCoder.decodeString(encoded);
if (method == 'z') return gzip.gunzipString(kelondroBase64Order.enhancedCoder.decode(encoded));
if (method == 'c') return descrambleString(key, encoded);
return null;
}
// --------------------------------------------------------
// Section: one-way encryption
// --------------------------------------------------------
public static String oneWayEncryption(String key) {
cryptbig crypter = new cryptbig(key);
String e = crypter.encryptString(key);
if (e.length() == 0) e = "0XXXX";
if (e.length() % 2 == 1) e += "X";
while (e.length() < 32) e = e + e;
char[] r = new char[16];
for (int i = 0; i < 16; i++) r[i] = e.charAt(2 * i + 1);
return new String(r);
}
// --------------------------------------------------------
// Section: command interface
// --------------------------------------------------------
private static void help() {
System.out.println("AnomicCrypt (2003) by Michael Christen");
System.out.println("Password-based encryption using the " + defaultMethod + "-method in standard java");
System.out.println("usage: crypt -h | -help");
System.out.println(" crypt -1 <passwd>");
System.out.println(" crypt -md5 <file>");
System.out.println(" crypt ( -es64 | -ds64 | -ec64 | -dc64 ) <string>");
System.out.println(" crypt ( -e | -d ) <key> <string>");
System.out.println(" crypt -enc <key> <file> \\");
System.out.println(" [-o <target-file> | -preserveFilename] \\");
System.out.println(" [-d <YYYYMMddHHmmSSsss> | -preserveDate] [-noZip]");
System.out.println(" crypt -dec <key> <file> \\");
System.out.println(" [-o <target-file> | -preserveFilename] \\");
System.out.println(" [-d <YYYYMMddHHmmSSsss> | -preserveDate]");
System.out.println(" crypt ( -info | -name | -size | -date | -edate ) \\");
System.out.println(" <key> <encrypted-file>");
}
private static void longhelp() {
// --line-help-- *---------------------------------------------------------------
System.out.println("AnomicCrypt (2003) by Michael Christen");
System.out.println("");
System.out.println("");
System.out.println("crypt -1 <passwd>");
System.out.println("");
System.out.println(" One-way encryption of the given password.");
System.out.println(" The result is computed by encoding the word with the word as");
System.out.println(" the password and repeating it until the length is greater");
System.out.println(" than 32. Then every second character is taken to compose the");
System.out.println(" result which has always the length of 16 characters.");
System.out.println("");
System.out.println("");
System.out.println("crypt -md5 <file>");
System.out.println("");
System.out.println(" MD5 digest according to RFC 1321. The resulting bytes are");
System.out.println(" encoded as two-digit hex and concatenated to a single string.");
System.out.println("");
System.out.println("");
System.out.println("crypt -ec64 <cardinal>");
System.out.println("");
System.out.println(" Encoding of a cardianal (a positive long integer) with the");
System.out.println(" built-in non-standard base-64 algorithm.");
System.out.println("");
System.out.println("");
System.out.println("crypt -dc64 <string>");
System.out.println("");
System.out.println(" Decoding of the given b64-coded string to a cardinal number.");
System.out.println("");
System.out.println("");
System.out.println("crypt -es64 <string>");
System.out.println("");
System.out.println(" Encoding of a given String to a b64 string.");
System.out.println("");
System.out.println("");
System.out.println("crypt -ds64 <string>");
System.out.println("");
System.out.println(" Decoding of a given b64-coded string to a normal string.");
System.out.println("");
System.out.println("");
System.out.println("crypt -e <key> <string>");
System.out.println("");
System.out.println(" Encryption of a given Unicode-String.");
System.out.println(" The given string is first encoded to an UTF-8 byte stream, then");
System.out.println(" encoded using a password based encryption and then finaly");
System.out.println(" encoded to b64 to generate a printable form.");
System.out.println(" The PBE method is " + defaultMethod + ".");
System.out.println("");
System.out.println("");
System.out.println("crypt -d <key> <string>");
System.out.println("");
System.out.println(" Decryption of a string.");
System.out.println(" The string is b64-decoded, " + defaultMethod + "-decrypted, ");
System.out.println(" and then transformed to an unicode string.");
System.out.println("");
System.out.println("");
System.out.println("crypt -enc <key> <file> \\");
System.out.println(" [-o <target-file> | -preserveFilename] \\");
System.out.println(" [-d <target-date YYYYMMddHHmmSSsss> | -preserveDate] [-noZip]");
System.out.println("");
System.out.println("");
System.out.println("");
System.out.println("");
System.out.println("crypt -dec <key> <file> \\");
System.out.println(" [-o <target-file> | -preserveFilename] \\");
System.out.println(" [-d <target-date YYYYMMddHHmmSSsss> | -preserveDate]");
System.out.println("");
System.out.println("");
System.out.println("crypt ( -info | -name | -size | -date | -edate ) <key> <encrypted-file>");
System.out.println("");
System.out.println("");
}
public static void main(String[] s) {
if (s.length == 0) {
help();
System.exit(0);
}
if ((s[0].equals("-h")) || (s[0].equals("-help"))) {
longhelp();
System.exit(0);
}
if (s[0].equals("-tc")) {
// list all available crypt mehtods:
Set methods = listCryptoMethods("Cipher");
System.out.println(methods.size() + " crypt methods:" + methods.toString());
testCryptMethods(methods);
System.exit(0);
}
if (s[0].equals("-random")) {
int count = ((s.length == 2) ? (Integer.parseInt(s[1])) : 1);
for (int i = 0; i < count; i++) System.out.println(randomSalt());
System.exit(0);
}
if (s[0].equals("-1")) {
if (s.length != 2) {help(); System.exit(-1);}
System.out.println(oneWayEncryption(s[1]));
System.exit(0);
}
if (s[0].equals("-ec64")) {
// generate a b64 encoding from a given cardinal
if (s.length != 2) {help(); System.exit(-1);}
System.out.println(kelondroBase64Order.standardCoder.encodeLong(Long.parseLong(s[1]), 0));
System.exit(0);
}
if (s[0].equals("-dc64")) {
// generate a b64 decoding from a given cardinal
if (s.length != 2) {help(); System.exit(-1);}
System.out.println(kelondroBase64Order.standardCoder.decodeLong(s[1]));
System.exit(0);
}
if (s[0].equals("-es64")) {
// generate a b64 encoding from a given string
if (s.length != 2) {help(); System.exit(-1);}
System.out.println(kelondroBase64Order.standardCoder.encodeString(s[1]));
System.exit(0);
}
if (s[0].equals("-ds64")) {
// generate a b64 decoding from a given string
if (s.length != 2) {help(); System.exit(-1);}
System.out.println(kelondroBase64Order.standardCoder.decodeString(s[1]));
System.exit(0);
}
if (s[0].equals("-ess")) {
// 'scramble' string
if (s.length != 3) {help(); System.exit(-1);}
long t = System.currentTimeMillis();
System.out.println(scrambleString(s[1], s[2]));
System.out.println("Calculation time: " + (System.currentTimeMillis() - t) + " milliseconds");
System.exit(0);
}
if (s[0].equals("-dss")) {
// 'descramble' string
if (s.length != 3) {help(); System.exit(-1);}
long t = System.currentTimeMillis();
System.out.println(descrambleString(s[1], s[2]));
System.out.println("Calculation time: " + (System.currentTimeMillis() - t) + " milliseconds");
System.exit(0);
}
if (s[0].equals("-e")) {
if (s.length != 3) {help(); System.exit(-1);}
System.out.println((new cryptbig(s[1])).encryptString(s[2]));
System.exit(0);
}
if (s[0].equals("-d")) {
if (s.length != 3) {help(); System.exit(-1);}
System.out.println((new cryptbig(s[1])).decryptString(s[2]));
System.exit(0);
}
if (s[0].equals("-md5")) {
// generate a public key from a password that can be used for encryption
if (s.length != 2) {help(); System.exit(-1);}
String md5s = serverCodings.encodeMD5Hex(new File(s[1]));
System.out.println(md5s);
System.exit(0);
}
if (s[0].equals("-enc")) {
if ((s.length < 3) || (s.length > 4)) {help(); System.exit(-1);}
String target;
if (s.length == 3) target = s[2] + ".crypt"; else target = s[3];
(new cryptbig(s[1])).encryptFile(s[2], target, true /*compress*/);
System.exit(0);
}
if (s[0].equals("-dec")) {
if ((s.length < 3) || (s.length > 4)) {help(); System.exit(-1);}
String target;
if (s.length == 3) {
if (s[2].endsWith(".crypt"))
target = s[2].substring(0, s[2].length() - 7);
else
target = s[2] + ".decoded";
} else {
target = s[3];
}
(new cryptbig(s[1])).decryptFile(s[2], target);
System.exit(0);
}
help(); System.exit(-1);
}
}