Utilizzando PROGUARD è possibile effettuare un efficace offuscamento del codice e anche comprimere nel caso sia necessario ottimizzare spazio.
Si utilizza da riga di comando.
Per usarlo ho per prima cosa scritto un progetto di prova, un semplice java project che effettua una criptazione utilizzando l'algoritmo AES con chiave a 16 bit.
Testato il programma ho creato il jar.
Quindi ho esportato il jar dentro la cartella lib di PROGUARD.
Ho inserito sempre qui sotto la lib di PROGUARD anche il jar commons-codec-1.3.jar che è utilizzato dal mio codice.
Quindi ho creato (sempre sotto la lib) un file denominato parametri.pro così definito
-injars cipher.jar
-outjars cipher_obs.jar
-libraryjars C:\Programmi\Java\jdk1.6.0_29\jre\lib\rt.jar;C:\Programmi\Java\jdk1.6.0_29\jre\lib\jce.jar;commons-codec-1.3.jar
-keep public class it.oasi.cipher.Cypher {
public static void main(java.lang.String[]);
}
injars e outjars rappresentano rispettivamente il jar da offuscare e il jar prodotto dall'offuscamento
libraryjars indica tutte le librerie (comprese quelle generali della jdk come si può vedere) utilizzate dal jar. Si noti che ho onserito anche il jce.jar per avere il supporto della classe javax.crypto.Cipher
keep serve a mantenere l'entry point del codice (in questo caso il codice main)
Per generare il jar offuscato posizionarsi via shell sulla directory lib e digitare il seguente comando
java -jar proguard.jar @parametri.pro
Il risultato è il seguente
Se provo a decompilare il jar offuscato ottengo qualcosa di vagamente incomprensibile:
import java.io.PrintStream;
import java.security.Key;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class Cypher
{
private static Cypher a;
private Key a()
{
(localObject = Calendar.getInstance()).set(1, 1978);
((Calendar)localObject).set(2, 3);
((Calendar)localObject).set(5, 20);
Object localObject = ((Calendar)localObject).getTime();
SimpleDateFormat localSimpleDateFormat = new SimpleDateFormat("dd-MM-yyyy");
localObject = (localObject = localSimpleDateFormat.format((Date)localObject) + "abcdre").getBytes();
return (Key)(localObject = new SecretKeySpec(localObject, "AES"));
}
public static void main(String[] paramArrayOfString)
{
if (a == null)
a = new Cypher();
Object localObject2 = "abilitatoDrillDown";
localObject2 = localObject2;
Object localObject1 = localObject1 = paramArrayOfString = a;
Cipher localCipher;
(localCipher = Cipher.getInstance("AES")).init(1, ((Cypher)localObject1).a());
localObject1 = localCipher.doFinal(((String)localObject2).getBytes());
String str = new String(new Base64().encode(localObject1));
localObject2 = str;
localObject1 = paramArrayOfString;
localObject1 = localObject2;
localObject2 = new Base64().decode(((String)localObject1).getBytes());
localObject1 = localObject1;
(localCipher = Cipher.getInstance("AES")).init(2, ((Cypher)localObject1).a());
paramArrayOfString = new String(localCipher.doFinal(localObject2));
System.out.println("Cifrato: " + str);
System.out.println("Decifrato: " + paramArrayOfString);
}
}
NB: in questo caso anche importando il jar su un nostro progetto sarà visibile solo il metodo main, per fare in modo che lo siano anche altri metodi occorre specificarli qui.
Esempio:
-keep public class it.oasi.cipher.Cypher {
public static void main(java.lang.String[]);
public static it.oasi.cipher.Cypher getInstance();
public java.lang.String decifra(java.lang.String );
}
Da notare come le variabili di input non vanno specificati così come i tipi, se disgraziatamente doveste farlo (ad esempio nel decifra dopo il tipo di input definire la variabile valore) si incappa nel seguente messaggio di errore
Error: Expecting separating ',' or closing ')' before 'valore' in line 8 of file
'parametri.pro',
included from argument number 1
Si utilizza da riga di comando.
Per usarlo ho per prima cosa scritto un progetto di prova, un semplice java project che effettua una criptazione utilizzando l'algoritmo AES con chiave a 16 bit.
Testato il programma ho creato il jar.
Quindi ho esportato il jar dentro la cartella lib di PROGUARD.
Ho inserito sempre qui sotto la lib di PROGUARD anche il jar commons-codec-1.3.jar che è utilizzato dal mio codice.
Quindi ho creato (sempre sotto la lib) un file denominato parametri.pro così definito
-injars cipher.jar
-outjars cipher_obs.jar
-libraryjars C:\Programmi\Java\jdk1.6.0_29\jre\lib\rt.jar;C:\Programmi\Java\jdk1.6.0_29\jre\lib\jce.jar;commons-codec-1.3.jar
-keep public class it.oasi.cipher.Cypher {
public static void main(java.lang.String[]);
}
injars e outjars rappresentano rispettivamente il jar da offuscare e il jar prodotto dall'offuscamento
libraryjars indica tutte le librerie (comprese quelle generali della jdk come si può vedere) utilizzate dal jar. Si noti che ho onserito anche il jce.jar per avere il supporto della classe javax.crypto.Cipher
keep serve a mantenere l'entry point del codice (in questo caso il codice main)
Per generare il jar offuscato posizionarsi via shell sulla directory lib e digitare il seguente comando
java -jar proguard.jar @parametri.pro
Il risultato è il seguente
Classe originale
import java.io.IOException;
import java.security.Key;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class Cypher
{
private static Cypher istanza;
public static Cypher getInstance(){
if(istanza==null){
istanza=new Cypher();
}
return istanza;
}
private Key getKey()
{
Date data = getDateForKey();
SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");
String chiave = sdf.format(data) + "abcdre";
byte[] rawdata = chiave.getBytes();
SecretKeySpec skeySpec = new SecretKeySpec(rawdata, "AES");
return skeySpec; }
private Date getDateForKey() {
Calendar c = Calendar.getInstance();
c.set(1, 1978);
c.set(2, 3);
c.set(5, 20);
return c.getTime(); }
private byte[] encrypt(String valore) throws Exception {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(1, getKey());
return cipher.doFinal(valore.getBytes());
}
private byte[] decrypt(byte[] valore) throws Exception {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(2, getKey());
return cipher.doFinal(valore);
}
public String cifra(String valore) throws Exception {
return toBase64(encrypt(valore));
}
public String decifra(String valore) throws Exception {
return new String(decrypt(fromBase64(valore)));
}
private String toBase64(byte[] valore)
{
return new String(new Base64().encode(valore));
}
private byte[] fromBase64(String valore)
throws IOException
{
return new Base64().decode(valore.getBytes());
}
public static void main(String[] args)
throws Exception
{
Cypher c=Cypher.getInstance();
String cifratoB64 = c.cifra("abilitatoDrillDown");
String decifrato = c.decifra(cifratoB64);
System.out.println("Cifrato: " + cifratoB64);
System.out.println("Decifrato: " + decifrato);
}
}
import java.security.Key;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class Cypher
{
private static Cypher istanza;
public static Cypher getInstance(){
if(istanza==null){
istanza=new Cypher();
}
return istanza;
}
private Key getKey()
{
Date data = getDateForKey();
SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");
String chiave = sdf.format(data) + "abcdre";
byte[] rawdata = chiave.getBytes();
SecretKeySpec skeySpec = new SecretKeySpec(rawdata, "AES");
return skeySpec; }
private Date getDateForKey() {
Calendar c = Calendar.getInstance();
c.set(1, 1978);
c.set(2, 3);
c.set(5, 20);
return c.getTime(); }
private byte[] encrypt(String valore) throws Exception {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(1, getKey());
return cipher.doFinal(valore.getBytes());
}
private byte[] decrypt(byte[] valore) throws Exception {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(2, getKey());
return cipher.doFinal(valore);
}
public String cifra(String valore) throws Exception {
return toBase64(encrypt(valore));
}
public String decifra(String valore) throws Exception {
return new String(decrypt(fromBase64(valore)));
}
private String toBase64(byte[] valore)
{
return new String(new Base64().encode(valore));
}
private byte[] fromBase64(String valore)
throws IOException
{
return new Base64().decode(valore.getBytes());
}
public static void main(String[] args)
throws Exception
{
Cypher c=Cypher.getInstance();
String cifratoB64 = c.cifra("abilitatoDrillDown");
String decifrato = c.decifra(cifratoB64);
System.out.println("Cifrato: " + cifratoB64);
System.out.println("Decifrato: " + decifrato);
}
}
Se provo a decompilare il jar offuscato ottengo qualcosa di vagamente incomprensibile:
import java.io.PrintStream;
import java.security.Key;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class Cypher
{
private static Cypher a;
private Key a()
{
(localObject = Calendar.getInstance()).set(1, 1978);
((Calendar)localObject).set(2, 3);
((Calendar)localObject).set(5, 20);
Object localObject = ((Calendar)localObject).getTime();
SimpleDateFormat localSimpleDateFormat = new SimpleDateFormat("dd-MM-yyyy");
localObject = (localObject = localSimpleDateFormat.format((Date)localObject) + "abcdre").getBytes();
return (Key)(localObject = new SecretKeySpec(localObject, "AES"));
}
public static void main(String[] paramArrayOfString)
{
if (a == null)
a = new Cypher();
Object localObject2 = "abilitatoDrillDown";
localObject2 = localObject2;
Object localObject1 = localObject1 = paramArrayOfString = a;
Cipher localCipher;
(localCipher = Cipher.getInstance("AES")).init(1, ((Cypher)localObject1).a());
localObject1 = localCipher.doFinal(((String)localObject2).getBytes());
String str = new String(new Base64().encode(localObject1));
localObject2 = str;
localObject1 = paramArrayOfString;
localObject1 = localObject2;
localObject2 = new Base64().decode(((String)localObject1).getBytes());
localObject1 = localObject1;
(localCipher = Cipher.getInstance("AES")).init(2, ((Cypher)localObject1).a());
paramArrayOfString = new String(localCipher.doFinal(localObject2));
System.out.println("Cifrato: " + str);
System.out.println("Decifrato: " + paramArrayOfString);
}
}
NB: in questo caso anche importando il jar su un nostro progetto sarà visibile solo il metodo main, per fare in modo che lo siano anche altri metodi occorre specificarli qui.
Esempio:
-keep public class it.oasi.cipher.Cypher {
public static void main(java.lang.String[]);
public static it.oasi.cipher.Cypher getInstance();
public java.lang.String decifra(java.lang.String );
}
Da notare come le variabili di input non vanno specificati così come i tipi, se disgraziatamente doveste farlo (ad esempio nel decifra dopo il tipo di input definire la variabile valore) si incappa nel seguente messaggio di errore
'parametri.pro',
included from argument number 1