package org.fnppl.opensdx.security;
/*
* Copyright (C) 2010-2015
* fine people e.V. <opensdx@fnppl.org>
* Henning Thieß <ht@fnppl.org>
*
* http://fnppl.org
*/
/*
* Software license
*
* As far as this file or parts of this file is/are software, rather than documentation, this software-license applies / shall be applied.
*
* This file is part of openSDX
* openSDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* openSDX 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 Lesser General Public License
* and GNU General Public License along with openSDX.
* If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Documentation license
*
* As far as this file or parts of this file is/are documentation, rather than software, this documentation-license applies / shall be applied.
*
* This file is part of openSDX.
* Permission is granted to copy, distribute and/or modify this document
* under the terms of the GNU Free Documentation License, Version 1.3
* or any later version published by the Free Software Foundation;
* with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
* A copy of the license is included in the section entitled "GNU
* Free Documentation License" resp. in the file called "FDL.txt".
*
*/
import java.io.*;
import java.math.BigInteger;
import java.util.*;
import java.security.*;
import org.bouncycastle.crypto.engines.*;
import org.bouncycastle.crypto.*;
import org.bouncycastle.crypto.modes.*;
import org.bouncycastle.crypto.encodings.*;
import org.bouncycastle.crypto.paddings.*;
import org.bouncycastle.crypto.params.*;
//import com.sun.crypto.provider.AESCipher;
import com.sun.org.apache.bcel.internal.generic.AASTORE;
/*
* hrmpf. most probably not used in this context - hooray for Rijndael_256 !!!
*/
/*
* @author Henning Thieß <ht@fnppl.org>
*
*/
public class SymmetricKey {
static {
SecurityHelper.ensureBC();
}
private final static int keybits = 256;//ok, doing so fails the aes128-rule and may fall into US-weapons-regulation
private final static int blockbits = 128;
private byte[] initVector = null;
private byte[] keyBytes = null;
private final byte[] my_buff = new byte[16];
private final byte[] my_buff2 = new byte[48];
PaddedBufferedBlockCipher aesCipher = null;
private int blockSize = -1;
public SymmetricKey(byte[] key_bytes, byte[] iv
) {
this.keyBytes = key_bytes;
this.initVector = iv;
CBCBlockCipher aesCBC = new CBCBlockCipher(new AESEngine());
KeyParameter kp = new KeyParameter(keyBytes);
ParametersWithIV aesCBCParams = new ParametersWithIV(kp, initVector);
aesCipher = new PaddedBufferedBlockCipher(
aesCBC,
new PKCS7Padding()
);
aesCipher.init(true, aesCBCParams);
blockSize = aesCipher.getBlockSize();
}
public static SymmetricKey getRandomKey() {
SecureRandom sc = new SecureRandom();//TODO HT 20.02.2011 - quite good, but should swirl it twice with tiger, or aes/rijndael itself
byte[] aes_key_bytes = new byte[keybits / 8]; //yep. please be aware of non-8-dividable bits - however, should be 128 for various reasons
byte[] iv = new byte[blockbits/8];
sc.nextBytes(aes_key_bytes);
sc.nextBytes(iv);
//now should swirl those byte one more time...
return new SymmetricKey(aes_key_bytes, iv);
}
public static SymmetricKey getKeyFromPass(char[] pass, byte[] iv) throws Exception {
if(iv.length != blockbits/8) {
throw new RuntimeException("Invalid InitVector-Size: "+iv.length+" expected: "+(blockbits/8));
}
byte[] aes_key_bytes = new byte[keybits / 8];
byte[] sha256 = SecurityHelper.getSHA256(String.valueOf(pass).getBytes("UTF-8"));
//System.err.println("getKeyFromPass:: ll.length:"+sha256.length+"\taes_key_bytes.length:"+aes_key_bytes.length);
for(int i=0; i<aes_key_bytes.length; i++) {
aes_key_bytes[i] = sha256[i];
}
SymmetricKey sk = new SymmetricKey(aes_key_bytes, iv);
//System.out.println("INITVECTOR: "+SecurityHelper.HexDecoder.encode(sk.initVector,':',-1));
//System.out.println("KEY: "+SecurityHelper.HexDecoder.encode(sk.keyBytes,':',-1));
return sk;
}
// public byte[] encrypt(byte[] in) throws Exception {
// int blockSize = aesCipher.getBlockSize();
// byte[] cipherTextBlock = new byte[blockSize];
// int size = aesCipher.getOutputSize(in.length);
//
// byte[] result = new byte[size];
// int olen = aesCipher.processBytes(in, 0, in.length, result, 0);
// olen += aesCipher.doFinal(result, olen);
//
//
// if (olen < size) {
// System.out.println("SymmetricKey::encrypt::olen!=size: "+olen+"!="+size);
//
// byte[] tmp = new byte[olen];
// System.arraycopy(result, 0, tmp, 0, olen);
// result = tmp;
// }
//
// return result;
// }
public int encrypt(byte[] in, byte[] out) throws Exception {
int size = aesCipher.getOutputSize(in.length);
if(out.length<size) {
throw new Exception("outbuffer.size("+out.length+") too small to hold "+size+" bytes");
}
int olen = aesCipher.processBytes(in, 0, in.length, out, 0);
olen += aesCipher.doFinal(out, olen);
if (olen < size) {
System.out.println("SymmetricKey::encrypt::olen!=size: "+olen+"!="+size);
//
// byte[] tmp = new byte[olen];
// System.arraycopy(result, 0, tmp, 0, olen);
// result = tmp;
}
return olen;
}
public void encrypt(InputStream in, OutputStream out) throws Exception {
int read = -1;
int or = 0;
int rr = 0;
while((read=in.read(my_buff)) != -1) {
rr += read;
int rg = aesCipher.processBytes(my_buff, 0, read, my_buff2, 0);
// System.err.println("READ: "+read);
// System.err.println("PROCESS_BYTES_RETURN: "+rg);
out.write(my_buff2, 0, rg);
or += rg;
}
// int oss = aesCipher.getOutputSize(rr);
// System.err.println("BYTES_WRITTEN_OVERALL: "+or);
// System.err.println("BYTES_READ_OVERALL: "+rr);
// System.err.println("AESCIPHER.getOutputSize("+rr+"): "+oss);
// int rest = oss - or;
read = aesCipher.doFinal(my_buff2, 0);
// System.err.println("READ_LAST: "+read);
out.write(my_buff2, 0, read);
}
// //TODO HT 2013-10-11 optimize for direct byte-buffer !!!
// public byte[] encrypt(byte[] b) throws Exception {
// ByteArrayOutputStream out = new ByteArrayOutputStream();
// encrypt(new ByteArrayInputStream(b), out);
// return out.toByteArray();
// }
public byte[] decrypt(byte[] b) throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
decrypt(new ByteArrayInputStream(b), out);
return out.toByteArray();
}
public void decrypt(InputStream in, OutputStream out) throws Exception {
// if(key.length!=initvector.length || key.length!=keybits/8) {
// throw new Exception("invalid params");
// }
CBCBlockCipher aesCBC = new CBCBlockCipher(new AESEngine());
KeyParameter kp = new KeyParameter(keyBytes);
ParametersWithIV aesCBCParams = new ParametersWithIV(kp, initVector);
PaddedBufferedBlockCipher aesCipher = new PaddedBufferedBlockCipher(
aesCBC,
new PKCS7Padding()
);
aesCipher.init(false, aesCBCParams);
// aesCipher.init(true, aesCBCParams); //TODO pad block corrupted error when false. WHY??
int read = -1;
byte[] buff = new byte[128/8];//blocksize
while((read=in.read(buff)) != -1) {
byte[] ou = new byte[buff.length];
// System.err.println("read: "+read);
int rg = aesCipher.processBytes(buff, 0, read, ou, 0);
out.write(ou, 0, rg);
// System.err.println("rg: "+rg);
}
buff = new byte[2*128/8];//blocksize
read = aesCipher.doFinal(buff, 0);
out.write(buff, 0, read);
}
public byte[] getInitVector() {
return initVector;
}
public byte[] getKeyBytes() {
return keyBytes;
}
public static void main(String[] args) throws Exception {
//test encryption of private key
AsymmetricKeyPair akp = AsymmetricKeyPair.generateAsymmetricKeyPair();
String initv = "00112233445566778899AABBCCDDEEFF";
String pp = "password";
SymmetricKey sk = SymmetricKey.getKeyFromPass(pp.toCharArray(), SecurityHelper.HexDecoder.decode(initv));
byte[] encPrivKey = akp.getEncrytedPrivateKey(sk);
byte[] decPrivKey = sk.decrypt(encPrivKey);
System.out.println("PUB_KEY_MODULUS : "+SecurityHelper.HexDecoder.encode(akp.getPublicModulus(),':',-1));
System.out.println("PUB_KEY_EXP : "+SecurityHelper.HexDecoder.encode(akp.getPublicExponent(),':',-1));
System.out.println("ENC_PRIV_KEY_EXP: "+SecurityHelper.HexDecoder.encode(encPrivKey,':',-1));
System.out.println("DEC_PRIV_KEY_EXP: "+SecurityHelper.HexDecoder.encode(decPrivKey,':',-1));
System.out.println("keyid : "+akp.getKeyID());
// SymmetricKey l = SymmetricKey.getRandomKey();
// System.out.println("INITVECTOR: "+SecurityHelper.HexDecoder.encode(l.initVector,':',-1));
// System.out.println("KEY: "+SecurityHelper.HexDecoder.encode(l.keyBytes,':',-1));
// byte[] sha256 = SecurityHelper.getSHA256(String.valueOf(pp.toCharArray()).getBytes("UTF-8"));
// System.out.println("pass: "+pp);
// System.out.println("key: "+SecurityHelper.HexDecoder.encode(sha256,'\0',-1));
// INITVECTOR: 1D8BEE695B7F4EFF6F7B947F1B197B97
// KEY: 9034F3A02E7DBD9870D7FC23FCD0E3CA5B9292F7F2314B495DBF042078632B24
// byte[] key = SecurityHelper.HexDecoder.decode("9034F3A02E7DBD9870D7FC23FCD0E3CA5B9292F7F2314B495DBF042078632B24");
// byte[] init = SecurityHelper.HexDecoder.decode("2A8BEE695B7F4EFF6F7B947F1B197B97");
//
// SymmetricKey l = new SymmetricKey(key, init);
////
byte[] test = "ich asda will encoded werden...".getBytes();
// byte[] test = "ich".getBytes();
//
// ByteArrayOutputStream bout = new ByteArrayOutputStream();
//
// ByteArrayInputStream bin = new ByteArrayInputStream(test);
// bout.reset();
// sk.encrypt(bin, bout);
//
// byte[] enc = bout.toByteArray();
byte[] enc = new byte[512];
// byte[] enc = sk.encrypt(test);
int r = sk.encrypt(test, enc);
byte[] enc_t = new byte[r];
System.arraycopy(enc, 0, enc_t, 0, r);
byte[] dec = sk.decrypt(enc_t);
System.out.println("BEFORE: "+(new String(test)));
System.out.println("ENC: "+SecurityHelper.HexDecoder.encode(enc_t,':',-1));
System.out.println("AFTER: "+(new String(dec)));
}
}