For my current project, I'm working on connecting to a MySQL database using Java.
I have a bit of code, in which I'm using Gradle to substitute sensitive database credentials into the .java file using ReplaceTokens to populate a new version of the file in the build directory and source that version (with the replaced "detokenized" values) to compile the .class file.
I do not anticipate anyone aside from the core dev team handling the source .java files, only the .war which contains the compiled .class files. However, from a peek at these .class files using vim, I can tell that the detokenized values are plainly visible in the compiled bytecode.
My question is, assuming a scenario in which my .class files could be retrieved from the server by a potentially malicious agent, is there any better method of tokenization that would give another layer of security to the database credentials?
For additional information, the MySQL DB is accessed through a socket only, so I do not expect a malicious agent could do anything with the DB credentials alone, but I would like to make it as difficult as possible to determine these credentials anyway.
Thank you for any advice! I'm still very new to Java and Gradle in general, but this project has already given me much insight into what can be done.
Here is some simple code that does base64 encoding / decoding
I am using Blowfish for the algo
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public static String encrypt(String text) throws Exception
{
SecretKeySpec sksSpec = new SecretKeySpec(key.getBytes(), algo );
Cipher cipher = Cipher.getInstance(algo);
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, sksSpec);
byte[] encrypt_bytes = cipher.doFinal(text.getBytes());
return new String( Base64.encodeBase64(encrypt_bytes) );
}
public static String decrypt(String encrypt_str) throws Exception
{
SecretKeySpec sksSpec = new SecretKeySpec(key.getBytes(), algo);
Cipher cipher = Cipher.getInstance(algo);
cipher.init(Cipher.DECRYPT_MODE, sksSpec);
return new String(cipher.doFinal( Base64.decodeBase64(encrypt_str.getBytes("UTF-8"))));
}
Related
I am currently trying to encrypt some string using Java JDK version is 1.8.0. The process is like this:
RSA-PrivateKey(MD5Hash(original-string))
I am using Cipher and BC for encrypting & adding padding. Since I would like to output the Cipher instance, and re-generate it in another class. So I use CipherOutputStream to output the encrypted String as well as the cipher instance.
The code is going well before I specify the following code.
public void outputCipherFile() throws IOException {
FileOutputStream fos = new FileOutputStream("output.txt");
CipherOutputStream cos = new CipherOutputStream(fos,cipher);
//this.encryptedString is the String I wish to encrypt, which datatype is byte[]
cos.write(this.encryptedString);
cos.close();
}
If I remove the method call to outputCipherFile(), the encryption works. But when I doing the above code statement, the follow errors occur:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: too much data for RSA block
at org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineUpdate(Unknown Source)
at javax.crypto.Cipher.update(Cipher.java:1832)
at javax.crypto.CipherOutputStream.write(CipherOutputStream.java:158)
at javax.crypto.CipherOutputStream.write(CipherOutputStream.java:144)
at com.domain.JavaEncryptionProject.outputCipherFile(JavaEncryptionProject.java:132)<5 internal calls>
I found some reference online about RSA is too short for encrypting string. But I am not quite understand why it works in encryption and decryption, fail in output the txt with CipherOutputStram. Please help.
Thank you so much.
I'm migrating my native Android game to libGDX. So I can't access the Android libraries anymore and I'm trying to replace android.util.Base64 by org.apache.commons.codec.binary.Base64. (I need Base64's encodeToString and decode methods.)
Unfortunately, with the new package I get this error:
java.security.InvalidKeyException: Illegal key size (using the same 24-character-key as I did before).
Here at stackoverflow they say it's probably because "Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 7" are missing. But if I use them, the users of my app have to install them, too.
Is there any easier solution? Why did it work before?
EDIT:
This is the code that leads to the InvalidKeyException:
javax.crypto.Cipher writer = Cipher.getInstance("AES/CBC/PKCS5Padding");
String keyOf24Chars = "abcdefghijklmnopqrstuvwx";
IvParameterSpec ivSpec = getIv();
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.reset();
byte[] keyBytes = md.digest(keyOf24Chars.getBytes("UTF-8"));
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES/CBC/PKCS5Padding");
// secretKey.getAlgorithm(): "AES/CBC/PKCS5Padding"
// secretKey.getFormat(): "RAW"
// secretKey.getEncoded().length: 32
writer.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec); // java.security.InvalidKeyException: Illegal key size
EDIT 2:
As explained in Maarten Bodewes' comment, Android has it's own implementation of the java and javax classes which apparently have no problem with 32 byte keys. After I have installed the "JCE Unlimited Strength Jurisdiction Policy Files 7" we are coming to the code that uses Base64 and causes this error: java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Base64.encodeToString:
String valueToEncode = "xyz";
byte[] secureValue;
try {
secureValue = writer.doFinal(valueToEncode.getBytes("UTF-8"));
} catch (Exception e) {
throw new SecurePreferencesException(e);
}
Base64 base64 = new Base64();
String secureValueEncoded = base64.encodeToString(secureValue);
But this method does exist (in BaseNCodec which Base64 extends):
public String encodeToString(final byte[] pArray) {
return StringUtils.newStringUtf8(encode(pArray));
}
How can I make Android use this method?
EDIT 3:
Finally I solved my problem by writing an interface and then using my old Android code (when compiling for Android). Check this example for libGDX: Interfacing with platform specific code.
No, there isn't an easier solution. You could use 3DES instead of AES (which I presume you are using) but you would be downgrading your security, and still be incompatible with the previous code. Downgrading security of AES to 128 is a better idea, but the incompatibility issue won't go away.
If you are not using the encryption/decryption in a third party library (e.g. JSSE for SSL or XML encryption) then you could directly use the Bouncy Castle or Spongy Castle API's. So that means directly using AESBlockCipher + a mode of encryption. Bouncy Castle doesn't have these kind of limitations - they are part of the Oracle Cipher implementation.
It was working before because Android doesn't have these kind of restrictions while Java 7/8 SE does.
I am trying to duplicate an encryption process that is working in Java over to iOS/OSX.
My Java code is as follows:
PublicKey publicKey = KeyFactory.getInstance("RSA").
generatePublic(new RSAPublicKeySpec(firstKeyInteger, secondKeyInteger));
// This always results in the public key OpenSSLRSAPublicKey{modulus=2b3b11f044.....58df890,publicExponent=10001}
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWITHSHA1ANDMGF1PADDING");
String stringToEncode = "EncodeThisString";
byte[] bytesToEncode = stringToEncode.getBytes("UTF-8");
cipher.init(cipher.PUBLIC_KEY, publicKey);
byte[] encrypted = cipher.doFinal(plain);
The first challenge i'm struggling with is how to use the public key in iOS. Can I just dump the modulus into NSData and use it? Or must I store it in the keychain first? (I don't really need to use the keychain unless I must). Or is there a method similar to generatePublic() were I can recreate the public key using the 2 integers?
Then would I use SecKeyEncrypt to encrypt? Whenever I add this to my project I get Implicit declaration warnings even though I import the Security framework.
Thanks
EDIT -----
I think I have managed to get a Base64 encoded public key, which I believe is what is in a PEM certificate. Now, how to use it.
I have a JAR file and it depends on 5 different text files. The JAR file read the text files and give the result. The only problem I have is that I want to hide text files so that no one can see it. Kindly suggest me how should I add these text files in-to the JAR package. If someone knows JAR-TO-EXE tool so that the text files are hided in-to the EXE then it is acceptable too.
I suggest you look into Serialization as a possible solution, here's a link.
http://www.tutorialspoint.com/java/java_serialization.htm
I'm not 100% sure it isnt possible to reconstruct your data anyway but... the content of the file will definently be gibberish when you open it (even if you don't wanna use it for this learn it anyways its very useful).
EDIT: I wanted to elaborate abit so you know what it basically is (if you don't)... imagine you have an arraylist containing objects (the data that you need to save, like your own employee class or whatever. Wih serialization you can basically serialize that list to a file and when you wanna load the data simply deserialize it back. The file with the saved data will contain gibberish to anyone who opens it. It's actually alot easier than working with an average file, where you usually have to handle reading each line etc. also remember you have to implement Serializable interface in the classes you need to do it with.
Made you a simple example, below I'm using an extremely simple class called testing which only contains a name:
public class Testing implements Serializable {
private String name;
public Testing(String name) {
this.setName(name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Now to serialize an object of this class, remember you can do this with an arraylist of the objects aswell but it won't work in a static context! So you can't test it in a main method fx.
File file = new File("test.dat");
if (!file.exists()) file.createNewFile();
try {
ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream("test.dat"));
out.writeObject(new Testing("testobject"));
} catch(Exception ex) {}
try {
ObjectInputStream in = new ObjectInputStream( new FileInputStream("test.dat"));
Testing loadedObject = (Testing) in.readObject();
System.out.println( loadedObject.getName() );
} catch(Exception ex){}
The above code can be tested within a main method if you wanna see it in action, just copy paste it and import the stuff needed.
EDIT: Should mention that I read it is possible to use a simple but secure encryption method by using a wrapper class before serializing the object. But you will have to read up on that, though imo it would be a very elegant solution to use.
You can 'hide' the files in the jar, but they will still be accessible. Just unzipping the jar will provide access to its contents. If these documents are in anyway 'sensitive' I would suggest looking at encrypting them.
Cryptography itself is a broad subject that requires real understanding to create a secure solution, but the following has a basic example of encrypting and decrypting data using java.
http://www.software-architect.net/articles/using-strong-encryption-in-java/introduction.html
EDIT
The link suggests using the following:
The same code to generate the 'aesKey':
String key = "Bar12345Bar12345"; // 128 bit key
// Create key and cipher
Key aesKey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
To encrypt a string:
// encrypt the text
String toEncrypt = "Your text.";
cipher.init(Cipher.ENCRYPT_MODE, aesKey);
byte[] encryptedBytes = cipher.doFinal(toEncrypt.getBytes());
String encrypted = new String(encryptedBytes);
To decrypt a string:
// decrypt the text
cipher.init(Cipher.DECRYPT_MODE, aesKey);
String decrypted = new String(cipher.doFinal(encrypted.getBytes()));
The stored text in the file will be completely unintelligible. There are other things to consider; for example if you store the key in a class it will be possible to extract it. But with all security measures, the complexity of creating it has to balanced against the difficulty involved in working against it. This level is ultimately dependant on the nature of the information you are securing.
If you simply want to package the text files inside the jar, and the secruity of them is not an issue, see the following (assuming you are using eclipse):
How to package resources in Jar properly
I am a total cryptography novice and was looking to have a simple (ha!) AESEncryption utility class that I could use for reading/writing files and string with AES keys. Something like:
String toEcnrypt = "This is a secret message!";
AESEcnryption aes = new AESEncryption(); // 256-bit by default
String encrypted = aes.encrypt(toEncrypt);
// Do some stuff
String shouldBeSameAsFirstString = aes.decrypt(encrypted);
The idea being that every time an AESEncryption is instantiated, a KeySpec is generated (and can be returned by the API for subsequent storage). Here's what I cooked up after examining the code of much, much brighter people than myself (so if you see your code here, thanks!):
public class AESEncryption {
private SecretKeySpec keySpec;
public AESEncryption()
{
super();
setKeySpec(AES256Encryption.generateAES256KeySpec());
}
// Uses 256-bit encryption by default.
public static SecretKeySpec generateAES256KeySpec()
{
// Stack variables
byte[] byteArray = new byte[16];
SecretKey oTmpKey = null;
KeyGenerator oKeyGen;
try
{
oKeyGen = KeyGenerator.getInstance("AES");
oKeyGen.init(256);
oTmpKey = oKeyGen.generateKey();
}
catch(Throwable oThrown)
{
throw new RuntimeException(oThrown);
}
byteArray = oTmpKey.getEncoded();
return new SecretKeySpec(byteArray, "AES");
}
public String encrypt(final String p_strPlaintext)
{
String strEncrypted = null;
try
{
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
strEncrypted = Base64.encodeBase64String(cipher
.doFinal(p_strPlaintext.getBytes()));
}
catch(Throwable oThrown)
{
System.out.println(oThrown.getMessage());
throw new RuntimeException(oThrown);
}
return strEncrypted;
}
}
For the Base64 En/Decoding I'm using Commons Codec - why? Because like I said I'm a crypto novice and that's the only thing I could find that seemed to get the job done!
When I use this code:
// This creates a unique key spec for this instance.
AESEncryption aes = new AESEncryption();
String toEncrypt = "blah";
// Throws a Throwable and prints the following to the console:
// "Illegal key size or default parameters"
String encrypted = aes.encrypt(toEncrypt);
I saw this question on SO where the asker had the same problem and I see that I may be missing the JCE. Knowing next to nothing about JCE, here's what I've collected:
The JCE is required for the AES algorithm to execute on the Java platform
The JCE downloads as a ZIP but really just contains two JARs
I put these 2 JARs (US_export_policy and local_policy) on my project's build path (Eclipse) and reran the code. Again the same problem. I know the linked article references installation instructions that recommended including these JARs in the JRE, but at runtime my app should only care about finding the JARs on the classpath - it shouldn't care about where it finds them on the classpath!
Is there anything I can do from inside Elcipse to make sure the JCE is available to my runtime classpath? Or am I way off base and have a bug in my code that is causing these errors?
You could simply use 128 bit AES keys. They are secure enough 99% of the time. Either that or use 256 bit keys and install the unlimited strength crypto files as indicated in the readme. If you could simply put them in the classpath everybody would simply copy the contents into their own libraries and skip the whole protection. They don't contain runnable code, just resources.
i'm pretty sure those jars are meaningless in the runtime classpath. they have to be installed in the jre installation dir.