KeyStore ks = KeyStore.getInstance("JCEKS");
ks.load(null, null);
SecretKey skInput = new SecretKeySpec("input".getBytes(), "DESede");
SecretKeyEntry skeInput = new KeyStore.SecretKeyEntry(skInput);
ks.setEntry("input_key", skeInput, new KeyStore.PasswordProtection("banana".toCharArray()));
FileOutputStream fos = new FileOutputStream("my.keystore");
pambks.store(fos, "password".toCharArray());
fos.flush();
fos.close();
The code shown above is trying to encode the input into a SecretKey and store into keystore. The code shown below is how I retrieve the key from keystore. But I do not know how to decode it back to original value?
FileInputStream fin = new FileInputStream("my.keystore");
KeyStore ks = KeyStore.getInstance("JCEKS");
ks.load(fin, "password".toCharArray());
SecretKeyEntry ske = (SecretKeyEntry) readks.getEntry("input_key", new KeyStore.PasswordProtection("banana".toCharArray()));
SecretKey sk = ske.getSecretKey();
I wasn't sure whether this is the correct way of doing encryption and decryption to SecretKey, please correct me if I'm wrong.
Reading back this Q/A I think I've misread the question.
You can get the byte representation of the key back by calling the getEncoded method on you key. After that it is possible to use one of the String constructors to revert it back to text. As said, you should not use a string as key. Note that DES keys are contain parity in the last bit, so this may change the result. To use strings for keys it is more advisable to use hexadecimals. Note that keys should have enough entropy, and a string is not likely to provide that.
There are several things not entirely correct in the code above:
You should not (ever) use "input".getBytes(). First of all, getBytes() uses a platform specific default character encoding. If you want to use a DES key as a String, decode it with a decoder of hexadecimals (e.g. in Apache Commons codec or Bouncy Castle).
You should use 24 bytes for DESede keys. "input".getBytes() does not return enough bytes.
For DES keys it pays to put the key specification through a KeyFactory if only to make sure that the parity bits are set. They you will be sure that the key is valid.
To get a good amount of key data, use PBKDF2 for passwords or use randomly generated keys.
If an encrypted key store is good enough storage for an encrypted key depends on the use case and the other security measures.
Related
I need to sign data using Java with the SHA-256 and EMSA-PKCS1-V1_5 encoding and RSA private key.
Also I need to use padding type 1 in EMSA-PKCS1-V1_5 which means that the data before signing should be appended by zeros (not random data, as in padding type 2).
The output of the signature on the same data should be the same.
What is equivalent in Java to this mechanism?
I was trying to sign using SHA256withRSA but the output on the same data is always different.
EDIT:
I am using the following code (data, sigAlg, and cert is always the same, so I assume that the signature should be always the same):
CMSSignedDataStreamGenerator generator = new CMSSignedDataStreamGenerator();
String sigAlg = "SHA256withRSA";
ContentSigner contentSigner = new JcaContentSignerBuilder(sigAlg).setProvider("BC").build(getPrivateKey());
generator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()).build(contentSigner, cert));
generator.addCertificates(new JcaCertStore(certs));
OutputStream out = generator.open(responseOutputStream);
out.write(data);
out.close();
we use some Networkcredentials in out App. I just decompiled the app and was able to see the Credentials like Name and Password. I do not really get how to prevent this. I think the word "obfuscator" is the direction which I have to go.
We test proguard but it does not have string encryption or am I wrong?
Is there an easy and free way to do this?
Thank you.
Sorry, but this simply does't work no matter what you'll try. If you obfuscate / encrypt the credentials, the program still must be able to decrypt them at run-time. Therefore, the encryption keys must also be in the generated bytecode somewhere and therefore it's possible to take them, and decrypt the credentials manually outside the program (or just step through the program and read the credentials once they're decrypted).
What you're trying to do is Security by Obscurity and it doesn't work.
Whatever you do, if the program can obtain the credentials at run-time without any external help, a skilled attacker can do the same given enough time.
What you should do:
Store the credentials in plain-text in a property file. Don't bother with encryption, it's pointless. You must make sure the db user you're using is read-only or add-only or something similar so you prevent any damage.
Let the user input the password. If it's not stored in the bytecode, it's safe. He could e.g. input his password and have an account in the db...
Use a safe and known authentication mechanism. Plaintext login+password is not that.
Don't let your application go anywhere near a DB. Set up a service somewhere, with an API, which would hold the read DB conenction. Your application could connect to that and get data via this API. This way, an attacker can't directly access your DB. He could call anything in the new service, though, so you must make sure there's no sensitive data accessible in there.
You should consider to encipher the username and the password: How to encrypt String in Java.
// bytes to encrypt
byte[] input;
// the key and the initialization vector
byte[] keyBytes;
byte[] ivBytes;
// initialize the Cipher
SecretKeySpec key = new SecretKeySpec(keyBytes, "DES");
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
// encryption
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] encrypted= new byte[cipher.getOutputSize(input.length)];
int enc_len = cipher.update(input, 0, input.length, encrypted, 0);
enc_len += cipher.doFinal(encrypted, enc_len);
// decryption
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
byte[] decrypted = new byte[cipher.getOutputSize(enc_len)];
int dec_len = cipher.update(encrypted, 0, enc_len, decrypted, 0);
dec_len += cipher.doFinal(decrypted, dec_len);
Usually, the key (bytes array) should be stored in a file that is only accessible on the specific instance where the server is running and not coded into the app source file.
Otherwise you can use hash (e.g: md5 or sha1) and store the fingerprint instead of the plain string:
// SHA1("hello world") -> "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed
This is a simple method which allows you to calculate the SHA1 hash of a string:
public static String SHA1(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(text.getBytes("iso-8859-1"));
byte[] hash = md.digest();
Formatter formatter = new Formatter();
for (byte b : hash)
formatter.format("%02x", b);
return formatter.toString();
}
Import java.io.UnsupportedEncodingException, java.security.MessageDigest and java.security.NoSuchAlgorithmException are required.
Your issue is related to encryption and not obfuscation. You may use this library to store the credentials in an encrypted way: http://www.jasypt.org/encrypting-configuration.html
There are different ways to pass the encryption key to it.
Otherwise, depending on your context, consider using different authentication mechanisms (SSO like) instead of login/password.
I'm making a project about security in Java. The Server receives a password and with this password is supposed to insert in MAC to verify the consistency of one specific file.
Mac m;
SecretKey sk;
sk = KeyGenerator.getInstance( "AES" ).keygenerator();/* what i don't want to use */
byte[]mac=null;
Mac m = Mac.getInstance("HmacSHA1");
m.init( password ); /* it's wrong */
m.update("work of security".getBytes());
mac = m.doFinal();
What I can understand is how to define a SecretKey to MAC init...
The two typical methods use to derive an AES key from a password are:
Using the raw bytes of the password. This option is not very strong (subject to trivial dictionary attacks) and relies on the password being exactly 128/192/256 bits.
Deriving the key using a function, such as PBKDF2.
You need to find out how the key is derived. The two options in code are:
Raw Bytes
SecretKey aesKey = new SecretKeySpec(password.getBytes(someCharset), "AES");
Derivation Function
Example PBKDF2:
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
SecretKey aesKey = factory.generateSecret(new PBEKeySpec(password, salt,
iterations, 256));
The salt value is a random byte array (i.e. perhaps eight bytes). The iterations can be increased to improve security at the expense of performance.
I need to generate an AES key in Java (Android) from salt and password given from .Net WebService. I need to have the same key as the key generated in .net with the same password and salt (using Rfc2898DeriveBytes and AesManaged()).
Here is my code in Android:
char[] passwordAsCharArray = password.toCharArray();
PBEKeySpec pbeKeySpec = new PBEKeySpec(passwordAsCharArray, salt, 1000, 256);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
SecretKeySpec secretKey = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
Here is code in .net:
byte[] keyBytes = Encoding.Unicode.GetBytes(key);
Rfc2898DeriveBytes derivedKey = new Rfc2898DeriveBytes(key, keyBytes);
AesManaged rijndaelCSP = new AesManaged();
rijndaelCSP.BlockSize = 128;
rijndaelCSP.KeySize = 256;
rijndaelCSP.Key = derivedKey.GetBytes(rijndaelCSP.KeySize / 8);
rijndaelCSP.IV = derivedKey.GetBytes(rijndaelCSP.BlockSize / 8);
ICryptoTransform decryptor = rijndaelCSP.CreateDecryptor();
When I compare both keys they are different. Any ideas how to generate on Android the same key as in .Net? (I know that the key which have been generated in .net is correct).
Number of iterations in .Net is 1000, salt and password are also the same as in Android.
Ok, it turned out that I dont need exactly the same key (as a byte array). I needed this to decrypt a file (in Java) which have been encrypted in .Net - with this key it gaves me Bad Padding Exception so I think the key was different and that causes the problem, but all I needed to do was to generate IV like a key - that solved my problem. Thanks for response!
It looks like you used the "key" (which should be a password) as a salt in your .NET code, whereas the Java part uses a specified salt. Furthermore, you specified the Unicode character set for decoding your salt, which is weird, the salt should be a random octet string (== byte array) from the beginning.
I would recommend you transform your password and random salt to byte arrays first, compare both using a hexadecimal representation (on your console, or in your debugger) and only then use those as input parameters for the PBKDF2 function in each. I would recommend an UTF-8 encoding for your password.
Always specify all parameters in cryptography, try not to use default, e.g. for the iteration count. If your input is off by a single bit, the output will be completely incorrect, and there is no way to tell which parameter was responsible.
It looks like the Java and .NET PBKDF2 "primitive" is identical on both platforms, there is working code out on the internet.
I'm writing a program which does both encryption and decryption in DES. The same key used during the encryption process should be used while decrypting too right? My problem is encryption and decryption are run on different machines. This is how the key is generated during the encryption process.
SecretKey key = KeyGenerator.getInstance("DES").generateKey();
So ,I thought I'll write the key to a file. But looks like I can typecast a SecretKey object to a String but not vice-versa! So, how do I extract the key contained in a text file? And pass as an input to this statement?
decipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
Or else is it possible to take the key as an input from the user during both the encryption and decryption process?
Do this:
SecretKey key = KeyGenerator.getInstance("DES").generateKey();
byte[] encoded = key.getEncoded();
// save this somewhere
Then later:
byte[] encoded = // load it again
SecretKey key = new SecretKeySpec(encoded, "DES");
But please remember that DES is unsecure today (it can be relatively easily bruteforced). Strongly consider using AES instead (just replace "DES" with "AES).