My solution of problems:
1) How to sign a file using .pem(X509) key from my own java application.
2) How to check program's signs and run only good-signed.
3) How to create an easy read/write database for half-encrypted information (for example with encrypted name but non encrypted telephone's number). The key must be created by AES algorithm and be kept on the keystore.
I
Using a .pem (X509) key for signing a file
&
Running signed program
&
Running signed program
The first task is to sign a file using bouncycastle library and generated .pem key. The second - create an applickation, which will run only signed programs. I looked through different search results and have decided to collect the knowledge in my own tutorial.
The idea of signing a file is pretty simple:
- Use hashing algorithm to generate a checksum for the file.
- Use the key object with the Signature class to sign the checksum.
So, let's do it.
1)Generating a checksum.
I have found information about this step here.
We will use SHA-256 hashing algorithm (which is believed to be the most secure hashing algorithm as this article is written) to generate a checksum for /mypath/privkeyA.pem
import java.security.KeyPair;
import java.security.MessageDigest;
import java.security.Security;
import java.security.Signature;
import java.util.Arrays;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMReader;
import org.bouncycastle.openssl.PasswordFinder;
private static String getHash(String file) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-256");
StringBuffer hexString;
try (FileInputStream fis = new FileInputStream(file)) {
byte[] dataBytes = new byte[1024];
int nread = 0;
while ((nread = fis.read(dataBytes)) != -1) {
md.update(dataBytes, 0, nread);
};
byte[] mdbytes = md.digest();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < mdbytes.length; i++) {
sb.append(Integer.toString((mdbytes[i] & 0xff) + 0x100, 16).substring(1));
}
System.out.println("Hex format : " + sb.toString());
hexString = new StringBuffer();
for (int i = 0; i < mdbytes.length; i++) {
hexString.append(Integer.toHexString(0xFF & mdbytes[i]));
}
byte[] shaDig = md.digest();
}
return hexString.toString();
}
2) The next step is signing the hash checksum.
I have found the source for this step here.
What will we do:
- Read the privkeyA.pem from file
- Load the private Key into our class, using the Passphrase to decrypt the key
- Use the key object with the Signature class to sign the message
public static void main(String[] args) throws Exception {
try {
if (args[0] != null) {
. . . (some not important code) . . .
Security.addProvider(new BouncyCastleProvider());
String file = args[0];
String message = getHash(file);
try (FileOutputStream fos = new FileOutputStream(file + ".sig")) {
File privateKey = new File("/mypath/privkeyA.pem");
KeyPair keyPair = readKeyPair(privateKey, "password".toCharArray());
Signature signature = Signature.getInstance("SHA256WithRSAEncryption");
signature.initSign(keyPair.getPrivate());
signature.update(message.getBytes());
byte[] signatureBytes = signature.sign();
fos.write(signatureBytes);
}
}
}
} catch (Exception e) {
System.out.println("ex");
}
}
For example if you want to sign firefox.exe, firefox.exe.sig with it's signature will be created.
private static KeyPair readKeyPair(File privateKey, char[] keyPassword) throws IOException {
FileReader fileReader = new FileReader(privateKey);
PEMReader r = new PEMReader(fileReader, new DefaultPasswordFinder(keyPassword));
try {
return (KeyPair) r.readObject();
} catch (IOException ex) {
throw new IOException("The private key could not be decrypted", ex);
} finally {
r.close();
fileReader.close();
}
}
private static class DefaultPasswordFinder implements PasswordFinder {
private final char[] password;
private DefaultPasswordFinder(char[] password) {
this.password = password;
}
@Override
public char[] getPassword() {
return Arrays.copyOf(password, password.length);
}
}
}
We will use verifyFile method for checking signature of programs we has deal too in task two. I use my old java programs at this role. But first let us see how to run other programs. That is really very easy:
Process p = Runtime.getRuntime().exec("pathToProgram");
p.waitFor();
Let us continue with our's example. We has two java programs with main classes GraficalInterfaceTest.class and RBTreeTest.class. Program runs if it was passed verification:
public static void main(String[] args) throws Exception {
try {
if (args[0] != null) {
if (args[0].equals("run")) {
try {
String pr1 = "GraficalInterfaceTest";
String pr2 = "RBTreeTest";
if (verifyFile(pr1 + ".class")) {
Process p = Runtime.getRuntime().exec("java " + pr1);
p.waitFor();
}
if (verifyFile(pr2 + ".class")) {
Process p = Runtime.getRuntime().exec("java " + pr2);
p.waitFor();
}
} catch (Exception ex) {
}
. . . (not important code) . . .
Let's have a look at verify method now:
private static boolean verifyFile(String file) throws Exception {
try {
byte[] signatureBytes;
try (FileInputStream fis = new FileInputStream(file + ".sig")) {
signatureBytes = new byte[fis.available()];
fis.read(signatureBytes);
}
Security.addProvider(new BouncyCastleProvider());
String message = getHash(file);
File privateKey = new File("/mypath/privkeyA.pem");
KeyPair keyPair = readKeyPair(privateKey, "password".toCharArray());
Signature verifier = Signature.getInstance("SHA256WithRSAEncryption");
verifier.initVerify(keyPair.getPublic());
verifier.update(message.getBytes());
if (verifier.verify(signatureBytes)) {
System.out.println("Signature is valid");
return true;
} else {
System.out.println("Signature is invalid");
return false;
}
} catch (Exception e) {
System.out.println("Signature is invalid");
return false;
}
}
It looks similar to signing code. Returns true if signature is valid, false if it is invalid or don't exist.
II
Reading simple half-encrypted by AES database
At first we need to create a keystore for the key, the key and a vector iv. This place was very helpful for me. First of all we can't use usual .jks store for private symmetrical key, we must use "JCEKS" instead. Let us create keystore and key using keytool util by typing this command:
keytool -genseckey -keyalg AES -alias myseckey -keysize 256 -keypass mykeypass -storetype jceks
-keystore mystore.jck -storepass mystorepass
The new key is
stored in a JCEKS keystore file mystore.jck with
password "mystorepass". The password that protects the secret key
is "mykeypass".
Vector iv can be created with openssl (by generating new AES-256 key).
Now, when we have keystore, key and vector we will need a Provider's jar from bouncycastle: http://www.bouncycastle.org/latest_releases.html.
Headers:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.Key;
import java.security.KeyStore;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.engines.RijndaelEngine;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import java.io.FileOutputStream;
import java.security.Key;
import java.security.KeyStore;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.engines.RijndaelEngine;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
The most important encryption/decryption methods:
private static byte[] decrypt(byte[] cipher, byte[] key, byte[] iv) throws Exception {
PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher(new CBCBlockCipher(
new RijndaelEngine(256)));
CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(key), iv);
aes.init(false, ivAndKey);
return cipherData(aes, cipher);
}
private static byte[] encrypt(byte[] plain, byte[] key, byte[] iv) throws Exception {
PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher(new CBCBlockCipher(
new RijndaelEngine(256)));
CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(key), iv);
aes.init(true, ivAndKey);
return cipherData(aes, plain);
}
private static byte[] cipherData(PaddedBufferedBlockCipher cipher, byte[] data)
throws Exception {
int minSize = cipher.getOutputSize(data.length);
byte[] outBuf = new byte[minSize];
int length1 = cipher.processBytes(data, 0, data.length, outBuf, 0);
int length2 = cipher.doFinal(outBuf, length1);
int actualLength = length1 + length2;
byte[] result = new byte[actualLength];
System.arraycopy(outBuf, 0, result, 0, result.length);
return result;
}
PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher(new CBCBlockCipher(
new RijndaelEngine(256)));
CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(key), iv);
aes.init(false, ivAndKey);
return cipherData(aes, cipher);
}
private static byte[] encrypt(byte[] plain, byte[] key, byte[] iv) throws Exception {
PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher(new CBCBlockCipher(
new RijndaelEngine(256)));
CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(key), iv);
aes.init(true, ivAndKey);
return cipherData(aes, plain);
}
private static byte[] cipherData(PaddedBufferedBlockCipher cipher, byte[] data)
throws Exception {
int minSize = cipher.getOutputSize(data.length);
byte[] outBuf = new byte[minSize];
int length1 = cipher.processBytes(data, 0, data.length, outBuf, 0);
int length2 = cipher.doFinal(outBuf, length1);
int actualLength = length1 + length2;
byte[] result = new byte[actualLength];
System.arraycopy(outBuf, 0, result, 0, result.length);
return result;
}
I used RijndaelEngine(256) instead of AESEngine() because the last one has only 128 bit block's length.
Next we will work with database. At first we need to load a key from the keystore, and vector. Next we have only to play with some portion of text. =)
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream("keyiv.dat")) {
byte[] iv;
iv = new byte[fis.available()];
fis.read(iv);
fis.close();
KeyStore ks = KeyStore.getInstance("JCEKS");
ks.load(new java.io.FileInputStream("mykeystore.jck"), "keystore".toCharArray());
Key key = ks.getKey("myseckey", "mykeypass".toCharArray());
if (args.length > 0) {
String i1 = "\n123456 ";
String i2 = "\n123123 ";
String p1 = "3";
String p2 = "4";
try (FileOutputStream fos = new FileOutputStream("DataBase.txt")) {
fos.write(i1.getBytes());
fos.write(encrypt(p1.getBytes(),key.getEncoded(),iv));
fos.write(i2.getBytes());
fos.write(encrypt(p2.getBytes(),key.getEncoded(),iv));
fos.close();
}
catch (Exception e) {}
} else {
try (FileInputStream is = new FileInputStream("DataBase.txt")) {
byte[] points = new byte[32];
byte[] id = new byte[7];
while(is.read() != -1) {
is.read(id);
System.out.print(new String(id));
is.read(points);
System.out.println(new String(decrypt(points,key.getEncoded(),iv)));
}
}
catch (Exception e) {}
}
} catch (Exception e) {
System.out.println("Cannot find initialization vector");
}
}
try (FileInputStream fis = new FileInputStream("keyiv.dat")) {
byte[] iv;
iv = new byte[fis.available()];
fis.read(iv);
fis.close();
KeyStore ks = KeyStore.getInstance("JCEKS");
ks.load(new java.io.FileInputStream("mykeystore.jck"), "keystore".toCharArray());
Key key = ks.getKey("myseckey", "mykeypass".toCharArray());
if (args.length > 0) {
String i1 = "\n123456 ";
String i2 = "\n123123 ";
String p1 = "3";
String p2 = "4";
try (FileOutputStream fos = new FileOutputStream("DataBase.txt")) {
fos.write(i1.getBytes());
fos.write(encrypt(p1.getBytes(),key.getEncoded(),iv));
fos.write(i2.getBytes());
fos.write(encrypt(p2.getBytes(),key.getEncoded(),iv));
fos.close();
}
catch (Exception e) {}
} else {
try (FileInputStream is = new FileInputStream("DataBase.txt")) {
byte[] points = new byte[32];
byte[] id = new byte[7];
while(is.read() != -1) {
is.read(id);
System.out.print(new String(id));
is.read(points);
System.out.println(new String(decrypt(points,key.getEncoded(),iv)));
}
}
catch (Exception e) {}
}
} catch (Exception e) {
System.out.println("Cannot find initialization vector");
}
}
I hope this was helpful.