/**
* Copyright 2009 Google Inc.
*
* 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 net.appjet.oui;
import scala.collection.mutable.ArrayBuffer;
import javax.crypto.Cipher;
import java.security._;
import java.security.spec._;
import java.math.BigInteger;
import java.io.{ObjectInputStream, ObjectOutputStream, FileInputStream, FileOutputStream, PrintWriter, OutputStreamWriter, ByteArrayOutputStream, ByteArrayInputStream, InputStream, InputStreamReader, BufferedReader, DataOutputStream, DataInputStream};
import net.appjet.common.util.BetterFile;
// object EncryptomaticTest {
// def main(args: Array[String]) {
// args(0) match {
// case "genkeys" => {
// val keyPair = Encryptomatic.generateKeyPair;
// println("made key pair.")
// Encryptomatic.writeKeyPair(keyPair, args(1), args(2));
// println("done.");
// }
// case "printkeys" => {
// val keyPair = Encryptomatic.generateKeyPair;
// val Pair(pubBytes, privBytes) = Encryptomatic.keyPairBytes(keyPair);
// println("Public key: "+Encryptomatic.bytesToAscii(pubBytes))
// println("Private key: "+Encryptomatic.bytesToAscii(privBytes));
// }
// case "sign" => {
// println(Encryptomatic.sign(java.lang.System.in, Encryptomatic.readPrivateKey(new FileInputStream(args(1)))));
// }
// case "verify" => {
// if (Encryptomatic.verify(java.lang.System.in, Encryptomatic.readPublicKey(new FileInputStream(args(1))), args(2))) {
// println("Verification succeeded.");
// } else {
// println("Verification failed.");
// }
// }
// case "test" => {
// val out = new PrintWriter(new OutputStreamWriter(System.out, "UTF-8"), true);
// val src = "Hey dudes, this is a test of この魚は築地からのですか?";
// out.println(src);
// val bytes = Encryptomatic.bytesToAscii(src.getBytes("UTF-8"));
// out.println("bytes: "+bytes);
// val done = new String(Encryptomatic.asciiToBytes(bytes), "UTF-8");
// out.println(done);
// out.println("Match? "+(done == src));
// }
// case "keytest" => {
// val keyPair = Encryptomatic.generateKeyPair;
// val bytes = Encryptomatic.keyPairBytes(keyPair);
// try {
// val newKeyPair = Encryptomatic.readKeyPair(new ByteArrayInputStream(Encryptomatic.bytesToAscii(bytes._1).getBytes()),
// new ByteArrayInputStream(Encryptomatic.bytesToAscii(bytes._2).getBytes()));
// println("equal? "+(keyPair.getPublic.getEncoded.deepEquals(newKeyPair.getPublic.getEncoded) && keyPair.getPrivate.getEncoded.deepEquals(newKeyPair.getPrivate.getEncoded)));
// } catch {
// case e: InvalidKeySpecException => {
// println("equality failed.")
// println("public key 1 is: "+bytes._1.mkString("(", ",", ")"));
// println("public key 2 is: "+BetterFile.getStreamBytes(new Encryptomatic.AsciiToBytesInputStream(new ByteArrayInputStream(Encryptomatic.bytesToAscii(bytes._1).getBytes()))).mkString("(", ",", ")"));
// println("pk1 enc to: "+Encryptomatic.bytesToAscii(bytes._1));
// }
// }
// }
// }
// }
// }
object Encryptomatic {
private val chars = "0123456789abcdefghijlkmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
def bytesToAscii(bytes: Array[Byte]) = {
var i = BigInt(bytes);
val neg = i < 0;
if (neg)
i = BigInt(0)-i;
val sb = new StringBuffer();
while (i > BigInt(chars.length-1)) {
val Pair(div, mod) = i /% BigInt(chars.length);
sb.append(chars(mod.intValue));
i = div;
}
sb.append(chars(i.intValue));
(if (neg) "-" else "")+sb.toString.reverse;
}
def asciiToBytes(src: String) = {
var i = BigInt(0);
val Pair(isNegative, b) =
if (src.startsWith("-"))
(true, src.substring(1))
else
(false, src);
for (c <- b) {
i = i * chars.length + chars.indexOf(c);
}
if (isNegative)
i = BigInt(0)-i;
i.toByteArray
}
def generateKeyPair(keyType: String) = {
val keyGen = KeyPairGenerator.getInstance(keyType);
val random = SecureRandom.getInstance("SHA1PRNG", "SUN");
keyGen.initialize(1024, random);
keyGen.generateKeyPair();
}
def keyPairBytes(keyPair: KeyPair) = {
val pubKey = keyPair.getPublic();
if (pubKey.getFormat != "X.509")
throw new RuntimeException("Can't produce public key in format: "+pubKey.getFormat);
val privKey = keyPair.getPrivate();
if (privKey.getFormat != "PKCS#8")
throw new RuntimeException("Can't produce private key in format: "+privKey.getFormat);
(pubKey.getEncoded, privKey.getEncoded)
}
def writeKeyPair(keyPair: KeyPair, publicKey: String, privateKey: String) {
val pubOutputStream = new PrintWriter(new FileOutputStream(publicKey));
val privOutputStream = new PrintWriter(new FileOutputStream(privateKey));
val Pair(pubBytes, privBytes) = keyPairBytes(keyPair);
pubOutputStream.print(bytesToAscii(pubBytes));
privOutputStream.print(bytesToAscii(privBytes));
List(pubOutputStream, privOutputStream).foreach(x => {x.flush(); x.close()});
}
class AsciiToBytesInputStream(in: InputStream) extends InputStream {
val reader = new BufferedReader(new InputStreamReader(in));
val bytes = new ByteArrayInputStream(asciiToBytes(reader.readLine()));
def read(): Int = bytes.read();
}
def readPublicKey(keyType: String, publicKey: InputStream) = {
val pubKeySpec = new X509EncodedKeySpec(BetterFile.getStreamBytes(new AsciiToBytesInputStream(publicKey)));
KeyFactory.getInstance(keyType).generatePublic(pubKeySpec);
}
def readPrivateKey(keyType: String, privateKey: InputStream) = {
val privKeySpec = new PKCS8EncodedKeySpec(BetterFile.getStreamBytes(new AsciiToBytesInputStream(privateKey)));
KeyFactory.getInstance(keyType).generatePrivate(privKeySpec);
}
def readKeyPair(keyType: String, publicKey: InputStream, privateKey: InputStream) = {
new KeyPair(readPublicKey(keyType, publicKey),
readPrivateKey(keyType, privateKey));
}
def sign(source: InputStream, key: PrivateKey): Array[byte] = {
val dsa = Signature.getInstance("SHA1withDSA");
dsa.initSign(key);
val inBytes = new Array[Byte](4096);
var count = source.read(inBytes);
while (count > 0) {
dsa.update(inBytes, 0, count);
count = source.read(inBytes);
}
dsa.sign();
}
def verify(source: InputStream, key: PublicKey, sig: Array[byte]): Boolean = {
val dsa = Signature.getInstance("SHA1withDSA");
dsa.initVerify(key);
val inBytes = new Array[Byte](4096);
var count = source.read(inBytes);
while (count > 0) {
dsa.update(inBytes, 0, count);
count = source.read(inBytes);
}
dsa.verify(sig)
}
def encrypt(source: InputStream, key: PublicKey): Array[byte] = {
val cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, key);
val inBytes = new Array[Byte](100);
val outBytesStream = new ByteArrayOutputStream();
val dataOut = new DataOutputStream(outBytesStream);
var count = source.read(inBytes);
while (count > 0) {
val arr = cipher.doFinal(inBytes, 0, count);
dataOut.writeShort(arr.length);
dataOut.write(arr, 0, arr.length);
count = source.read(inBytes);
}
dataOut.writeShort(0);
outBytesStream.toByteArray();
}
def decrypt(source: InputStream, key: PrivateKey): Array[byte] = {
val in = new DataInputStream(source);
def readBlock() = {
val length = in.readShort();
if (length > 0) {
val bytes = new Array[Byte](length);
in.readFully(bytes);
Some(bytes);
} else {
None;
}
}
val outBytes = new ArrayBuffer[Byte];
val cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, key);
var block = readBlock();
while (block.isDefined) {
outBytes ++= cipher.doFinal(block.get);
block = readBlock();
}
outBytes.toArray;
}
}
object Encryptor {
def main(args: Array[String]) {
args(0) match {
case "genkeys" => {
println("generating keys...");
val keyPair = Encryptomatic.generateKeyPair(args(1));
println("saving public key to: "+args(2)+"; private key to: "+args(3));
Encryptomatic.writeKeyPair(keyPair, args(2), args(3));
println("done.");
}
case "test" => {
val plaintext = "This is a test of some data that's actually pretty long once you really start thinking about it. I mean, it needs to be more than 117 bytes for it to be a reasonable test, and I suppose it's pretty close to that now. OK, let's just go for it and see what happens.".getBytes("UTF-8");
val keys = Encryptomatic.generateKeyPair("RSA");
val ciphertext = Encryptomatic.bytesToAscii(Encryptomatic.encrypt(new ByteArrayInputStream(plaintext), keys.getPublic()));
println(ciphertext);
println(new String(Encryptomatic.decrypt(new ByteArrayInputStream(Encryptomatic.asciiToBytes(ciphertext)), keys.getPrivate()), "UTF-8"));
}
case "decode" => {
val key = Encryptomatic.readPrivateKey(args(1), new FileInputStream(args(2)));
val plaintext = Encryptomatic.decrypt(new ByteArrayInputStream(Encryptomatic.asciiToBytes(args(3))), key);
println(new String(plaintext, "UTF-8"));
}
case "decodeFile" => {
println("Enter private key (assuming type RSA):");
val key = Encryptomatic.readPrivateKey("RSA", java.lang.System.in);
val file = new java.io.File(args(1));
println("Reading "+file.getName()+"...");
val reader = new java.io.BufferedReader(new java.io.InputStreamReader(new FileInputStream(file)));
var line = reader.readLine();
while (line != null) {
val bytes = Encryptomatic.decrypt(new ByteArrayInputStream(Encryptomatic.asciiToBytes(line)), key);
println(new String(bytes, "UTF-8"));
line = reader.readLine();
}
}
}
}
}