import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
import java.util.Scanner;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class secretMesages {
private static Scanner sc;
public static void main(String[] args) throws UnsupportedEncodingException, InvalidKeyException, IllegalBlockSizeException, NoSuchAlgorithmException, NoSuchPaddingException, BadPaddingException, InvalidAlgorithmParameterException {
sc = new Scanner(System.in);
while(true){
System.out.print("Password: ");
byte password []=sc.nextLine().getBytes("UTF-8");
byte key []=take(digest512(password));
long ms = System.currentTimeMillis();
//multi-threaded key stretching
Stretcher [] threads=new Stretcher[8];
for(int i=0; i<8; i++){
threads[i]=new Stretcher(key);
key=digest512(key);
threads[i].start();
}
key=new byte[32]; //without this line it would have too many elements
while(true){
if(!threads[0].isAlive() && !threads[1].isAlive() && !threads[2].isAlive() && !threads[3].isAlive() && !threads[4].isAlive() && !threads[5].isAlive() && !threads[6].isAlive() && !threads[7].isAlive()){
for(int i=0; i<key.length; i++){
if(i<key.length/8.)
key[i]=threads[0].getResult()[i];
else if(i<2*key.length/8.)
key[i]=threads[1].getResult()[i];
else if(i<3*key.length/8.)
key[i]=threads[2].getResult()[i];
else if(i<4*key.length/8.)
key[i]=threads[3].getResult()[i];
else if(i<5*key.length/8.)
key[i]=threads[4].getResult()[i];
else if(i<6*key.length/8.)
key[i]=threads[5].getResult()[i];
else if(i<7*key.length/8.)
key[i]=threads[6].getResult()[i];
else
key[i]=threads[7].getResult()[i];
}
break;
}
/*
* this actually speeds up the key stretching process by preventing this driver method
* from wasting resources
*/
try{
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Key generated from password in " + ((double)(System.currentTimeMillis() - ms))/1000 + " seconds");
System.out.print("Encrypt or Decrypt (e/d)? ");
String choice=sc.next();
sc = new Scanner(System.in);
if(choice.equalsIgnoreCase("e")){
System.out.print("Cleartext: ");
byte [] encryptedData=encrypt(sc.nextLine().getBytes("UTF-8"),key);
System.out.print("Obfuscated text: ");
for(byte b: encryptedData){
System.out.print(b + " ");
}
System.out.println();
}
else if (choice.equalsIgnoreCase("d")){
System.out.print("Obfuscated text: ");
String dataS=sc.nextLine();
String [] dataSA=dataS.split(" ");
byte [] dataBA=new byte[dataSA.length];
for(int i=0; i<dataSA.length; i++){
dataBA[i]=Byte.parseByte(dataSA[i]);
}
sc = new Scanner(System.in);
System.out.print("Initialisation vector: ");
String ivS=sc.nextLine();
String [] ivSA=ivS.split(" ");
byte [] ivBA=new byte[ivSA.length];
for(int i=0; i<ivSA.length; i++){
ivBA[i]=Byte.parseByte(ivSA[i]);
}
byte [] decryptedByteArray=decrypt(dataBA, key, ivBA);
System.out.println("Cleartext: " + new String(decryptedByteArray, "UTF-8"));
}
else{
System.out.println("WUT?!");
}
System.out.println();
}
}
static byte[] encrypt(byte[] data, byte[] key) throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException {
Cipher c = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec k = new SecretKeySpec(key, "AES");
c.init(Cipher.ENCRYPT_MODE, k);
byte[] encryptedData = c.doFinal(data);
byte[] iv = c.getIV();
System.out.print("Initialisation vector: ");
for(byte b: iv){
System.out.print(b + " ");
}
System.out.println();
return encryptedData;
}
static byte[] decrypt(byte[] encryptedData, byte[] key, byte[] iv) throws InvalidKeyException, IllegalBlockSizeException, NoSuchAlgorithmException, NoSuchPaddingException, BadPaddingException, InvalidAlgorithmParameterException{
Cipher c = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec k = new SecretKeySpec(key, "AES");
c.init(Cipher.DECRYPT_MODE, k, new IvParameterSpec(iv));
return c.doFinal(encryptedData);
}
static byte[] digest512(byte[] message) {// sha2 hash -- too lazy to learn sha3
byte[] hashed = new byte[64], block = new byte[128], padded = padMessage2(message);
long[] K = {
0x428A2F98D728AE22L, 0x7137449123EF65CDL, 0xB5C0FBCFEC4D3B2FL,
0xE9B5DBA58189DBBCL, 0x3956C25BF348B538L, 0x59F111F1B605D019L,
0x923F82A4AF194F9BL, 0xAB1C5ED5DA6D8118L, 0xD807AA98A3030242L,
0x12835B0145706FBEL, 0x243185BE4EE4B28CL, 0x550C7DC3D5FFB4E2L,
0x72BE5D74F27B896FL, 0x80DEB1FE3B1696B1L, 0x9BDC06A725C71235L,
0xC19BF174CF692694L, 0xE49B69C19EF14AD2L, 0xEFBE4786384F25E3L,
0x0FC19DC68B8CD5B5L, 0x240CA1CC77AC9C65L, 0x2DE92C6F592B0275L,
0x4A7484AA6EA6E483L, 0x5CB0A9DCBD41FBD4L, 0x76F988DA831153B5L,
0x983E5152EE66DFABL, 0xA831C66D2DB43210L, 0xB00327C898FB213FL,
0xBF597FC7BEEF0EE4L, 0xC6E00BF33DA88FC2L, 0xD5A79147930AA725L,
0x06CA6351E003826FL, 0x142929670A0E6E70L, 0x27B70A8546D22FFCL,
0x2E1B21385C26C926L, 0x4D2C6DFC5AC42AEDL, 0x53380D139D95B3DFL,
0x650A73548BAF63DEL, 0x766A0ABB3C77B2A8L, 0x81C2C92E47EDAEE6L,
0x92722C851482353BL, 0xA2BFE8A14CF10364L, 0xA81A664BBC423001L,
0xC24B8B70D0F89791L, 0xC76C51A30654BE30L, 0xD192E819D6EF5218L,
0xD69906245565A910L, 0xF40E35855771202AL, 0x106AA07032BBD1B8L,
0x19A4C116B8D2D0C8L, 0x1E376C085141AB53L, 0x2748774CDF8EEB99L,
0x34B0BCB5E19B48A8L, 0x391C0CB3C5C95A63L, 0x4ED8AA4AE3418ACBL,
0x5B9CCA4F7763E373L, 0x682E6FF3D6B2B8A3L, 0x748F82EE5DEFB2FCL,
0x78A5636F43172F60L, 0x84C87814A1F0AB72L, 0x8CC702081A6439ECL,
0x90BEFFFA23631E28L, 0xA4506CEBDE82BDE9L, 0xBEF9A3F7B2C67915L,
0xC67178F2E372532BL, 0xCA273ECEEA26619CL, 0xD186B8C721C0C207L,
0xEADA7DD6CDE0EB1EL, 0xF57D4F7FEE6ED178L, 0x06F067AA72176FBAL,
0x0A637DC5A2C898A6L, 0x113F9804BEF90DAEL, 0x1B710B35131C471BL,
0x28DB77F523047D84L, 0x32CAAB7B40C72493L, 0x3C9EBE0A15C9BEBCL,
0x431D67C49C100D4CL, 0x4CC5D4BECB3E42B6L, 0x597F299CFC657E2AL,
0x5FCB6FAB3AD6FAECL, 0x6C44198C4A475817L
};
long[] H = {
0x6A09E667F3BCC908L, 0xBB67AE8584CAA73BL,
0x3C6EF372FE94F82BL, 0xA54FF53A5F1D36F1L,
0x510E527FADE682D1L, 0x9B05688C2B3E6C1FL,
0x1F83D9ABFB41BD6BL, 0x5BE0CD19137E2179L
};
for (int i = 0; i < padded.length / 128; i++) {
long[] words = new long[80];
long a = H[0], b = H[1], c = H[2], d = H[3], e = H[4], f = H[5], g = H[6], h = H[7], T1, T2;
System.arraycopy(padded, 128 * i, block, 0, 128);
for (int j = 0; j < 16; j++) {
words[j] = 0;
for (int k = 0; k < 8; k++) {
words[j] |= ((block[j * 8 + k] & 0x00000000000000FFL) << (56 - k * 8));
}
}
for (int j = 16; j < 80; j++) {
words[j] = Sigma1(words[j-2]) + words[j-7] + Sigma0(words[j-15]) + words[j-16];
}
for (int j = 0; j < 80; j++) {
T1 = h + Ch(e, f, g) + Sum1(e) + words[j] + K[j];
T2 = Sum0(a) + Maj(a, b, c);
h = g;
g = f;
f = e;
e = d + T1;
d = c;
c = b;
b = a;
a = T1 + T2;
}
H[0] += a;
H[1] += b;
H[2] += c;
H[3] += d;
H[4] += e;
H[5] += f;
H[6] += g;
H[7] += h;
}
for (int i = 0; i < 8; i++) {
System.arraycopy(longToBytes(H[i]), 0, hashed, 8*i, 8);
}
return hashed;
}
private static long Sigma0(long l) {
return Long.rotateRight(l, 1) ^ Long.rotateRight(l, 8) ^ (l >>> 7);
}
private static long Sigma1(long l) {
return Long.rotateRight(l, 19) ^ Long.rotateRight(l, 61) ^ (l >>> 6);
}
private static long Sum0(long a) {
return Long.rotateRight(a, 28) ^ Long.rotateRight(a, 34) ^ Long.rotateRight(a, 39);
}
private static long Sum1(long e) {
return Long.rotateRight(e, 14) ^ Long.rotateRight(e, 18) ^ Long.rotateRight(e, 41);
}
private static long Ch(long e, long f, long g) {
return (e & f) ^ ((~e) & g);
}
private static long Maj(long a, long b, long c) {
return (a & b) ^ (a & c) ^ (b & c);
}
private static byte[] padMessage2(byte[] data){
int origLength = data.length;
int tailLength = origLength % 128;
int padLength = 0;
if((64 - tailLength >= 9))
padLength = 128 - tailLength;
else
padLength = 128 - tailLength;
byte[] thePad = new byte[padLength];
thePad[0] = (byte)0x80;
long lengthInBits = origLength * 8;
for (int i = 0; i < 8; i++) {
thePad[thePad.length - 1 - i] = (byte) ((lengthInBits >> (8 * i)) & 0xFFL);
}
byte[] output = new byte[origLength + padLength];
System.arraycopy(data, 0, output, 0, origLength);
System.arraycopy(thePad, 0, output, origLength, thePad.length);
return output;
}
public static byte[] longToBytes(long l) {
byte[] b = new byte[8];
for (int c = 0; c < 8; c++) {
b[c] = (byte) ((l >>> (56 - 8 * c)) & 0xffL);
}
return b;
}
static byte[] take(byte[] A) {
byte[] C= new byte[32];
Random rnd=new Random(A[0]);//A acts as a seed -- the Random class gives psudo random numbers
rnd.setSeed(rnd.nextLong());//complications are your friend
for(int i=0; i<32; i++){
C[i]=A[rnd.nextInt(64)];
}
return C;
}
}
class Stretcher extends Thread{
public byte[] key;
public Stretcher(byte input[]){
key=input;
}
public void run() {
for(int i=0; i<1000000; i++){ //change the number of iterations to make finding the key easier or harder
key=secretMesages.take(secretMesages.digest512(key));
}
}
public byte[] getResult(){
return key;
}
}