First and foremost, I am not a Java programmer. I'm looking for an example solution to this problem because the Java developer I have does not have much experience working with encryption. Everything we've found online pertains to encrypting web pages and dealing with the MS keystore. We just want to work with a single string from PowerBuilder (PB) and be able to decrypt it in Java. The restriction here is the MS library. Due to certain limitations, we are stuck with using this method of encrypting so it's up to the Java side to handle what's being thrown at it.
What I have is a PB version 10.2 program that needs to call this Java utility and pass it a username & password. We are trying to encrypt the password as a command line friendly string as that is how PB will make the call to the Java app.
In PB I'm using the following object:
http://www.topwizprogramming.com/freecode_crypto.html
What the code is doing, is wrapping the Microsoft cryptographic API found in advapi32.dll. The functions it uses are:
CryptAcquireContext
http://msdn.microsoft.com/en-us/library/aa379886(VS.85).aspx
CryptCreateHash
http://msdn.microsoft.com/en-us/library/aa379908(VS.85).aspx
CryptHashData
http://msdn.microsoft.com/en-us/library/aa380202(VS.85).aspx
CryptDeriveKey
http://msdn.microsoft.com/en-us/library/aa379916(VS.85).aspx
CryptEncrypt
http://msdn.microsoft.com/en-us/library/aa379924(VS.85).aspx
It's using the Microsoft Strong Cryptographic Provider and PROV_RSA_FULL. The code takes the data to be encrypted, converts it to a BLOB which is then passed to the encryption functions. There, it acuires a context, creates a hash object from the context, hashes the password, gets a session key from the hash, then calls encrypt/decrypt. Last thing is it takes the BLOB returned and converts it to a string under the ANSI character set.
There are a number of constants which at a glance I understand where some come from, others not so much:
Constant String KEY_CONTAINER = "MyKeyContainer"
Constant ULong PROV_RSA_FULL = 1
Constant ULong CALG_MD5 = 32771
Constant ULong CALG_RC4 = 26625
Constant ULong ENCRYPT_ALGORITHM = CALG_RC4
Constant ULong CRYPT_NEWKEYSET = 8
Constant ULong ERROR_MORE_DATA = 234
Whether this is done in 1.5 using something like BouncyCastle or 1.6 with the Sun crypto interface for MS I don't care, we're just dying to see this work and are honestly over our heads.
Hey i need to crypt a string and store it a file and then I need to read the file again and decrypt the same string back.
But i dont want to crypt the whole file. Once I have got the required crypted value stored in the file ,i need to convert that alone to the original string.
Can you please help me out with the sample code.
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class Decrypt
{
public static void main(String... argv)
throws Exception
{
byte[] password = "password".getBytes("UTF-8");
byte[] ciphertext = { -68, -112, 66, 78, 85, 50, 22, -63,
16, 24, -45, 4, -116, -14, 88, 34,
-85, 116, 105, 59, 45, -126 };
byte[] plaintext = decrypt(password, ciphertext);
System.out.println(new String(plaintext, "UTF-8"));
}
public static byte[] decrypt(byte[] password, byte[] ciphertext)
throws GeneralSecurityException
{
MessageDigest digest = MessageDigest.getInstance("MD5");
byte[] hash = digest.digest(password);
Cipher rc4 = Cipher.getInstance("RC4");
rc4.init(Cipher.DECRYPT_MODE, new SecretKeySpec(hash, "RC4"));
return rc4.doFinal(ciphertext);
}
}
N.B.:
This "encryption" is horrible. RC4 is a key stream cipher. Never use the same keystream cipher key for more than one message! Using the same password in this way for multiple messages makes it trivial to recover the plain text and the key given multiple cipher texts. Given the weakness in MD5, they can probably recover the password too. These flaws are enough to compromise a good stream cipher, but RC4, like MD5, has its own vulnerabilities and is not recommended for new applications.
I'm sure that you knew all that, and are constrained by some legacy application, but if other people see this answer, they need to understand that the PowerBuilder "crypto" library you are compelled to use is incompetently implemented.
Since cipher input and output is always "binary", a text encoding is commonly used, such as Base-64 or Base-85 when the cipher text has to pass through a text-oriented channel (like the command line). If possible, can you Base-64 encode the cipher text before invoking the Java utility? That would insulate you from any character encoding issues.
Related
As a bit of context to this I am converting a java file to python and am on the last operation. I'm at about 200 LOC so it makes it that much more edge of the seat...
Anyways, in java the operation is:
Cipher cipher = Cipher.getInstance("AES/ecb/nopadding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keys[i], "AES"));
//doFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)
cipher.doFinal(save, saveOffset, 16, save, saveOffset);
In python I have this:
from Crypto.Cipher import AES
cipher = AES.new(bytes(keys[i]), AES.MODE_ECB)
cipher.decrypt(?????)
This is taken from the package under decrypt():
:Parameters:
ciphertext : bytes/bytearray/memoryview
The piece of data to decrypt.
The length must be multiple of the cipher block length.
:Keywords:
output : bytearray/memoryview
The location where the plaintext must be written to.
If ``None``, the plaintext is returned.
:Return:
If ``output`` is ``None``, the plaintext is returned as ``bytes``.
Otherwise, ``None``.
As you can see .decrypt() doesn't really have an input for offsets, but I was wondering if there was some way around this?
This is why I decided to post on SO, would I be able to send:
temp_bytes = save[offset]
temp_decrypt = cipher.decrypt(temp_bytes)
save[offset] = temp_decrypt
Or when decrypting does it use the whole file as context and i will get the wrong output? I would love to just do it and test it but the output is just gibberish that i will have to write another program to parse (another java to python project).
What ended up working was:
save[saveOffset:saveOffset+16] = cipher.decrypt(save[saveOffset:saveOffset+16])
Did not expect it to work so easily
I've got a Java application that does AES-256-OCB. For this, the BouncyCastle crypto library is used. As-is, it uses the standard JCA interface, but this requires a special policy file to be installed to permit key sizes greater than 128 bits.
This is unsuitable in our environment, and it seems to me that we may be able to dodge this by using BouncyCastle's own lightweight API. I'm a bit confused by this API, however, and I was curious how I actually go about instantiating a cipher as AES/OCB/NoPadding.
I'm normally pretty good about reading documentation, but BouncyCastle's rather extensive options have me a bit confused.
How can I instantiate a BlockCipher object for 256-bit OCB mode with no padding, using the BouncyCastle lightweight API, and use this to encrypt and decrypt data? I've already got the key, IV and data as byte[]s.
Here's what I came up with reading through BouncyCastle's test code. It appears to function, although I've not compared the results with any test vectors.
Call with encrypt=true for encryption, encrypt=false for decryption. Set tagLen to the desired length of the AEAD tag in bits (eg. tagLen=128). Optionally set ad to associated data for validation, or leave null to skip. Returns a properly-sized byte array of resulting ciphertext or plaintext.
protected static byte[] processCipher(boolean encrypt, int tagLen, byte[] keyBytes, byte[] iv, byte[] in, byte[] ad) throws IllegalStateException, InvalidCipherTextException {
KeyParameter key = new KeyParameter(keyBytes);
AEADParameters params = new AEADParameters(key, tagLen, iv);
AEADBlockCipher cipher = new OCBBlockCipher(new AESEngine(), new AESEngine());
cipher.init(encrypt, params);
byte[] out = new byte[cipher.getOutputSize(in.length)];
if(ad != null) cipher.processAADBytes(ad, 0, ad.length);
int offset = cipher.processBytes(in, 0, in.length, out, 0);
offset += cipher.doFinal(out, offset);
return out;
}
So basically I have these snippets of code and would like them to produce the same output:
require 'openssl'
aes = OpenSSL::Cipher::Cipher.new("AES-128-CBC")
aes.key = "aaaaaaaaaaaaaaaa"
aes.iv = "aaaaaaaaaaaaaaaa"
aes.encrypt
encrypted = aes.update("1234567890123456") << aes.final
puts encrypted.unpack('H*').join
This prints:
8d3bbffade308f8e4e80cb77ecb8df19ee933f75438cec1315c4a491bd1b83f4
And this Java code:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
String key = "aaaaaaaaaaaaaaaa";
String textToEncryptpt = "1234567890123456";
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
IvParameterSpec ivspec = new IvParameterSpec(key.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
byte[] encrypted = cipher.doFinal(textToEncryptpt.getBytes());
System.out.println(Crypto.bytesToHex(encrypted));
Prints:
2d3760f53b8b3dee722aed83224f418f9dd70e089ecfe9dc689147cfe0927ddb
Annoying thing is that it was working a couple of days ago... so I am not sure what happened. What's wrong with this code? Do you see anything unusual?
Ruby script is wrong. You have to first call the encrypt method, and then set the key and iv:
require 'openssl'
aes = OpenSSL::Cipher::Cipher.new("AES-128-CBC")
aes.encrypt
aes.key = "aaaaaaaaaaaaaaaa"
aes.iv = "aaaaaaaaaaaaaaaa"
encrypted = aes.update("1234567890123456") << aes.final
puts encrypted.unpack('H*').join
I figured out because when trying to decode an encrypted string I got:
aescrypt.rb:13:in `final': bad decrypt (OpenSSL::Cipher::CipherError)
from aescrypt.rb:13:in `<main>'
Seems you found already the reason that your script does give different results.
Some more things to consider:
Don't ever hardcode the key in the program - that way you can't easily change it, and if someone gets access to your program code, she also gets to see the key.
Don't ever use a constant initialization vector. Instead, generate a random one and send it together with the ciphertext. Alternatively, if you generate the key from a password and some salt, you can also generate the IV from the same ... but don't use the key directly as IV.
Your key/IV values are strings, not bytes. String.getBytes() (in Java) converts the string to bytes using some encoding. The encoding used is system-dependent, and none of the usual String encodings (UTF-8, Latin-1, ...) can represent all bytes as printable (and typeable) characters. Preferably use something like Base64 or hex-encoding, if you have to store your key as string.
And whenever you transform a string to bytes, specify an encoding (and use the same encoding later for retrieving it).
#Cristian, For key and initial vector, you can create a function by using today's date plus the secure fixed keyword.
Eg: key = January 8, 2012 + Key
And for initial vector,
Eg: iv = January 8, 2012 + IV
Then enter that data(key and iv) to MD5 it will produce the output 16 bytes that you can use for the Key and IV. Every day, key and iv will change randomly.
Make sure both systems use the same date format and setup on the same date.
I'm having a problem with MessageDigest returning different hash values on different computers.
One computer is running 32-bit Java on Windows Vista and the other is running 64-bit Java on Mac OS. I'm not sure if it is because MessageDigest is machine dependent, or I need to explicitly specify a character encoding somewhere, or perhaps something else. Here's the
code:
public static boolean authenticate(String salt, String encryptedPassword,
char[] plainTextPassword ) throws NoSuchAlgorithmException {
// do I need to explcitly specify character encoding here? -->
String saltPlusPlainTextPassword = salt + new String(plainTextPassword);
MessageDigest sha = MessageDigest.getInstance("SHA-512");
// is this machine dependent? -->
sha.update(saltPlusPlainTextPassword.getBytes());
byte[] hashedByteArray = sha.digest();
// or... perhaps theres a translation problem here? -->
String hashed = new String(hashedByteArray);
return hashed.equals(encryptedPassword);
}
Should this code execute differently on these two different machines?
If it is machine dependent the way I've written it, is there another way hash these passwords that is more portable? Thanks!
Edit:::::
This is the code I'm using to generate the salts:
public static String getSalt() {
int size = 16;
byte[] bytes = new byte[size];
new Random().nextBytes(bytes);
return org.apache.commons.codec.binary.Base64.encodeBase64URLSafeString(bytes);
}
Solution:::
Thanks to the accepted solution, I was able to fix my code:
public static boolean authenticate_(String salt, String encryptedPassword,
char[] plainTextPassword ) throws NoSuchAlgorithmException, UnsupportedEncodingException {
// This was ok
String saltPlusPlainTextPassword = salt + new String(plainTextPassword);
MessageDigest sha = MessageDigest.getInstance("SHA-512");
// must specify "UTF-8" encoding
sha.update(saltPlusPlainTextPassword.getBytes("UTF-8"));
byte[] hashedByteArray = sha.digest();
// Use Base64 encoding here -->
String hashed = org.apache.commons.codec.binary.Base64.encodeBase64URLSafeString(hashedByteArray);
return hashed.equals(encryptedPassword);
}
Encodings are causing you problems. First here:
saltPlusPlainTextPassword.getBytes()
That will use the default encoding for the machine. Bad idea. Specify "UTF-8" as a simple solution. (It's guaranteed to be present.)
Next this causes issues:
String hashed = new String(hashedByteArray);
hashedByteArray is arbitrary binary data. To safely convert it to text, either use a base-64 encoding or just hex. Again, you're currently using the default encoding, which will vary from machine to machine. There are loads of 3rd party libraries for base64 encoding in Java.
Likely Jon Skeet's solution above is the cause, and his recommendations should definitely be taken into account, but another possible cause is a misunderstanding of salt.
Salt is a semi-secret random value that is applied to a String prior to hashing. This makes it harder to perform a brute force attack when trying to guess what an originating String was because the salt is presumably unknown to the attacker.
Salt values generally differ installation to installation. Its possible that the actual cause is just that you have the salt values set differently on the different machines.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 5 years ago.
Improve this question
I have a program that reads server information from a configuration file and would like to encrypt the password in that configuration that can be read by my program and decrypted.
Requirements:
Encrypt plaintext password to be stored in the file
Decrypt the encrypted password read in from the file from my program
How would I go about doing this? I was thinking of writing my own algorithm, but I feel it would be terribly insecure.
A simple way of doing this is to use Password Based Encryption in Java. This allows you to encrypt and decrypt a text by using a password.
This basically means initializing a javax.crypto.Cipher with algorithm "AES/CBC/PKCS5Padding" and getting a key from javax.crypto.SecretKeyFactory with the "PBKDF2WithHmacSHA512" algorithm.
Here is a code example (updated to replace the less secure MD5-based variant):
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class ProtectedConfigFile {
public static void main(String[] args) throws Exception {
String password = System.getProperty("password");
if (password == null) {
throw new IllegalArgumentException("Run with -Dpassword=<password>");
}
// The salt (probably) can be stored along with the encrypted data
byte[] salt = new String("12345678").getBytes();
// Decreasing this speeds down startup time and can be useful during testing, but it also makes it easier for brute force attackers
int iterationCount = 40000;
// Other values give me java.security.InvalidKeyException: Illegal key size or default parameters
int keyLength = 128;
SecretKeySpec key = createSecretKey(password.toCharArray(),
salt, iterationCount, keyLength);
String originalPassword = "secret";
System.out.println("Original password: " + originalPassword);
String encryptedPassword = encrypt(originalPassword, key);
System.out.println("Encrypted password: " + encryptedPassword);
String decryptedPassword = decrypt(encryptedPassword, key);
System.out.println("Decrypted password: " + decryptedPassword);
}
private static SecretKeySpec createSecretKey(char[] password, byte[] salt, int iterationCount, int keyLength) throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
PBEKeySpec keySpec = new PBEKeySpec(password, salt, iterationCount, keyLength);
SecretKey keyTmp = keyFactory.generateSecret(keySpec);
return new SecretKeySpec(keyTmp.getEncoded(), "AES");
}
private static String encrypt(String property, SecretKeySpec key) throws GeneralSecurityException, UnsupportedEncodingException {
Cipher pbeCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
pbeCipher.init(Cipher.ENCRYPT_MODE, key);
AlgorithmParameters parameters = pbeCipher.getParameters();
IvParameterSpec ivParameterSpec = parameters.getParameterSpec(IvParameterSpec.class);
byte[] cryptoText = pbeCipher.doFinal(property.getBytes("UTF-8"));
byte[] iv = ivParameterSpec.getIV();
return base64Encode(iv) + ":" + base64Encode(cryptoText);
}
private static String base64Encode(byte[] bytes) {
return Base64.getEncoder().encodeToString(bytes);
}
private static String decrypt(String string, SecretKeySpec key) throws GeneralSecurityException, IOException {
String iv = string.split(":")[0];
String property = string.split(":")[1];
Cipher pbeCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
pbeCipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(base64Decode(iv)));
return new String(pbeCipher.doFinal(base64Decode(property)), "UTF-8");
}
private static byte[] base64Decode(String property) throws IOException {
return Base64.getDecoder().decode(property);
}
}
One problem remains: Where should you store the password that you use to encrypt the passwords? You can store it in the source file and obfuscate it, but it's not too hard to find it again. Alternatively, you can give it as a system property when you start the Java process (-DpropertyProtectionPassword=...).
The same issue remains if you use the KeyStore, which also is protected by a password. Basically, you will need to have one master password somewhere, and it's pretty hard to protect.
Check out jasypt, which is a library offering basic encryption capabilities with minimum effort.
Yes, definitely don't write your own algorithm. Java has lots of cryptography APIs.
If the OS you are installing upon has a keystore, then you could use that to store your crypto keys that you will need to encrypt and decrypt the sensitive data in your configuration or other files.
I think that the best approach is to ensure that your configuration file (containing your password) is only accessible to a specific user account. For example, you might have an application-specific user, appuser, to which only trusted people have the password (and to which they su to).
That way, there isn't any annoying cryptography overhead and you still have a password which is secure.
I am assuming that you are not exporting your application configuration outside of a trusted environment (which I'm not sure would make any sense, given the question).
The big point, and the elephant in the room and all that, is that if your application can get hold of the password, then a hacker with access to the box can get hold of it too!
The only way somewhat around this, is that the application asks for the "master password" on the console using Standard Input, and then uses this to decrypt the passwords stored on file. Of course, this completely makes is impossible to have the application start up unattended along with the OS when it boots.
However, even with this level of annoyance, if a hacker manages to get root access (or even just access as the user running your application), he could dump the memory and find the password there.
The thing to ensure, is to not let the entire company have access to the production server (and thereby to the passwords), and make sure that it is impossible to crack this box!
Well to solve the problems of master password - the best approach is not to store the password anywhere, the application should encrypt passwords for itself - so that only it can decrypt them. So if I was using a .config file I would do the following, mySettings.config:
encryptTheseKeys=secretKey,anotherSecret
secretKey=unprotectedPasswordThatIputHere
anotherSecret=anotherPass
someKey=unprotectedSettingIdontCareAbout
so I would read in the keys that are mentioned in the
encryptTheseKeys, apply the Brodwalls example from above on them and
write them back to the file with a marker of some sort (lets say crypt:) to let the application know not to do it again, the output would look like this:
encryptTheseKeys=secretKey,anotherSecret
secretKey=crypt:ii4jfj304fjhfj934fouh938
anotherSecret=crypt:jd48jofh48h
someKey=unprotectedSettingIdontCareAbout
Just make sure to keep the originals in your own secure place...
See what is available in Jetty for storing password (or hashes) in configuration files, and consider if the OBF encoding might be useful for you. Then see in the source how it is done.
http://www.eclipse.org/jetty/documentation/current/configuring-security-secure-passwords.html
Try using ESAPIs encryption methods. It's easy to configure and you can also easily change your keys.
http://owasp-esapi-java.googlecode.com/svn/trunk_doc/latest/org/owasp/esapi/Encryptor.html
You
encrypt
decrypt
sign
unsign
hashing
time based signatures and much more with just one library.
Depending on how secure you need the configuration files or how reliable your application is, http://activemq.apache.org/encrypted-passwords.html may be a good solution for you.
If you are not too afraid of the password being decrypted and it can be really simple to configure using a bean to store the password key. However, if you need more security you can set an environment variable with the secret and remove it after launch. With this you have to worry about the application / server going down and not application not automatically relaunching.
If you are using Java 8, the use of the internal Base64 encoder and decoder can be avoided by replacing
return new BASE64Encoder().encode(bytes);
with
return Base64.getEncoder().encodeToString(bytes);
and
return new BASE64Decoder().decodeBuffer(property);
with
return Base64.getDecoder().decode(property);
Note that this solution doesn't protect your data as the methods for decrypting are stored in the same place. It just makes it more difficult to break. Mainly it avoids to print it and show it to everybody by mistake.