I am new to encryption.
I have looked at the javax.crypto documentation and got encryption of a file to work using this code ...
File saveFile = new File("Settings.set");
saveFile.delete();
FileOutputStream fout = new FileOutputStream(saveFile);
//Encrypt the settings
//Generate a key
byte key[] = "My Encryption Key98".getBytes();
DESKeySpec desKeySpec = new DESKeySpec(key);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey skey = keyFactory.generateSecret(desKeySpec);
//Prepare the encrypter
Cipher ecipher = Cipher.getInstance("DES");
ecipher.init(Cipher.ENCRYPT_MODE, skey);
// Seal (encrypt) the object
SealedObject so = new SealedObject(this, ecipher);
ObjectOutputStream o = new ObjectOutputStream(fout);
o.writeObject(so);
o.close();
However if you were a clever hacker ( or maybe even amateur since I figured this out), all you would have to do is open the class file that contains this code, and the encryption key (My Encryption Key98) is plainly visible.
How do you encrypt the encryption key? ...LOL... Can you?
Thanks for your help!
If the attacker has access to both the software and the file, it could decrypt it. There are some ways to solve this:
Use asymetric keys. Encrypt the file with the public key, and it can only be decrypted with a private key. This assumes that the software does not need to decrypt the file.
Use Diffie-Hellman exchange. If you want to send an encrypted piece of data over the network, both parties can establish a key without an attacker knowing about it.
If the program needs to both encrypt and decrypt the data, there is nothing you can do. The attacker can simply run the program and look at the decrypted information.
An attacker can always do everything the program can do and usually quite a bit more. The only way to get things secure is the use information not under control of the program. Request the user to enter a password or put information in a store under control of the operating system. The later will not help if an attacker has physical access or maybe even a lot of rights unless special hardware like a Trusted Platform Module (TPM) is involved.
Well if the program can decrypt the data without additional input from the user, you can't really avoid someone else from accessing the file if he has access to the program.
If you are targeting Windows only, you might want to take a look at the Data Protection API (DPAPI). It essentially does the same thing, but the passphrase used for encryption is protected by the operating system on a user (or machine) scope. Simply put: you need the user login (or a program that runs on the given user account) to access the key (or for machine scope the login for any user on the machine).
I don't know how to access the API from Java, but Google brings up some wrapper libraries.
Don't hardcode the key. Assuming you don't have a user on hand to enter the passphrase, configure your code to pull the encryption key from a plain file, then rely on operating system security to keep the file safe. Provide a way to migrate to a new key when the system administrator deems it necessary.
I do not believe that this is possible without having the user entering the key on encryption and decryption.
You could employ some technique to make it harder to view the key without the full source code, but it would not be secure.
If your program can encrypt / decrypt a file on its own, then everything you need to perform the decryption is already built into the program, so a determined troublemaker could decrypt files you encrypted.
If possible, ask the user for a 'password,' and use what they give you as the encryption / decryption key.
Is it important that the user not be able to see his own encryption key? Or merely important that by discovering his won key, the user should not thereby know everyone else's key?
You could prompt the user for a personal key and either store it externally or prompt the user each time you need it. That way each user's key would be his own, and would not be usable to decrypt documents stored by other users on other machines.
the most secure method is not use any encryption, just put your user.properties to your home directory, with following code:
String userhome = System.getProperty("user.home");
String username = system.getProperty("user.name");
String hostname = java.net.InetAddress.getLocalHost().getHostName();
if (hostname.equals("webserver") && username.equals("root")){
ResourceBundle user = ResourceBundle.getBundle(userhome/ "user.properties");
}
Related
I am trying to develop an android app in java which needs encryption. Also I want to use AES-256 for encryption. But when I look a tutorial of it, It always generates a random key. My question is: How can I decrypt a string if I encrypt it with a random key? Also I tried almost every code in web, but none of them worked, so can you provide a AES-256 encryption code with no salt and IV. If I know something wrong, please correct me and teach me the truth.
EDIT: I am trying to make a password manager app. App has two passwords, first one is the master password that we use for encryption string data. Second one is the passwords that we want to manage. Master password is stored in users mind. And other password will be stored in the app with encrypted version.
When user wants to see his passwords he will input his master key to decrypt the encrypted passwords. So how can I do it? And user's master password will be 32 or 64 digit and I don't think we need to generate a random key. Can you show me some way? I am not native english speaker, sorry for my bad english. Thanks for help.
As you have already discovered, a random key doesn't make sense for your app. Instead, you want to derive a key from the master password. To that end, you use a key derivation function or KDF.
In order to prevent dictionary attacks, you must also use a salt. A salt is a random value that you generate once and then store in your app (without any encryption).
Create salt
void initSalt()
{
byte[] salt = new byte[32];
new SecureRandom().nextBytes(salt);
... save salt ...
}
Get key for encryption
SecretKey getSecretKey(String masterPassword)
{
byte[] salt = ... retrieve saved salt ...
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
char[] chars = password.toCharArray();
int hashIterations = 1024;
int keySizeBytes = 32;
return keyFactory.generateSecret(
new PBEKeySpec(chars, salt, hashIterations, keySizeBytes * 8));
}
The getSecretKey() function is call both when the password manager is first setup as well as on any later use. The function will always return the same secret key if the salt doesn't change.
Some Android versions might not support PBKDF2WithHmacSHA256. If so, you have to revert to PBKDF2withHmacSHA1.
With the secret key generated from the master password, you can then go ahead an encrypt the data that the password manager wants to store.
The plain text is signed using java.security.Signature. Below is the code used to sign the plain text
public String getSignature(String plainText) throws Exception
{
KeyStore keyStore = loadKeyStore(); // A local method to read the keystore file from file system.
PrivateKey privateKey = (PrivateKey) keyStore.getKey(KEY_ALIAS_IN_KEYSTORE, KEYSTORE_PASSWORD.toCharArray());
Signature privateSignature = Signature.getInstance(SIGNATUREALGO);
privateSignature.initSign(privateKey);
privateSignature.update(plainText.getBytes("UTF-8"));
byte[] signature = privateSignature.sign();
return String.valueOf(signature);
// KEY_ALIAS_IN_KEYSTORE, KEYSTORE_PASSWORD and SIGNATUREALGO are all constant Strings
}
Note 1: I found online a way to verify the signature using the public key Java Code Examples for java.security.Signature#verify(). But this is not what I require.
Note 2: I also found a ways to encrypt and decrypt as mentioned here RSA Signing and Encryption in Java. But the use case I have in hand is to get the original plain text from a signed data. Is that possible?
No, you can't retrieve the original content from just the signature.
The signature alone does not contain enough information to restore the original clear text, no matter what keys you have access to.
The basic idea of a signature is to send it together with the clear text. That means the clear text will be visible, but the signature can be used to verify that the message was written (or at least signed) by who claims to have done so and has not been tampered with since then.
Signing something is different from encrypting it. The two often uses the same or related technologies and both fall under cryptography.
So I have a very basic openssl command that was provided to me openssl smime -encrypt -binary -aes-256-cbc -in $inPath -out $encryptedPath -outform DER $pubCert, this command also works correctly and outputs an encrypted file. I need to use the equivalent of this command in a java application, preferably without invoking process and using openssl itself (only because I feel like that is probably bad practice).
I have researched quite a lot and there does not seem to be any equivalent out there that I can find.. I have tried several things and most of them do not seem to work. The weird thing is... I am able to get a simple "Hello World" string to encrypt using the code I wrote (although I don't believe it was encrypting it correctly because I had the cipher set to "RSA" not "AES") but when the byte array was coming from a file, it silently failed and just wrote 0 bytes. Right now this is what my code looks like.
Cipher aes = Cipher.getInstance("RSA");
CertificateFactory certF = CertificateFactory.getInstance("X.509");
File public_cert = new File( getClass().getClassLoader().getResource("public.crt").getFile());
FileInputStream certIS = new FileInputStream(public_cert);
X509Certificate cert = (X509Certificate) certF.generateCertificate(certIS);
certIS.close();
aes.init(Cipher.ENCRYPT_MODE, cert);
File tarGz = new File("C:\\volatile\\generic.tar.gz");
FileInputStream fis = new FileInputStream(tarGz);
byte[] tarGzBytes = FileUtils.readFileToByteArray(tarGz);
tarGzBytes = "Hello World".getBytes();
ByteArrayInputStream bais = new ByteArrayInputStream("Hello World".getBytes());
File encFile = new File("C:\\volatile\\generic.tar.gz.enc");
FileOutputStream enc = new FileOutputStream(encFile);
CipherOutputStream cos = new CipherOutputStream(enc, aes);
cos.write(tarGzBytes);
//IOUtils.copy(fis, cos);
//IOUtils.copy(bais, cos);
cos.flush();
cos.close();
So this works, and encrypts a little file with Hello World encrypted in it. I don't believe this is AES-256-CBC though, and it does not work when I use the FileUtils.readFileToByteArray(tarGz), although the resulting byte array in a debugger is correctly sized at about 94MB. Which seems really odd to me, that it works with "Hello World".toByteArray() and not FileUtils.readAllBytes(tarGz). Also as a side note, the ByteArrayInputStream using IOUtils.copy works, whereas the FileInputStream version writes 0 bytes as well.
Also, when I set the cipher mode to AES/CBC/PKCS5Padding (because I found something online suggesting to set it to that and it looks more like what I want) I get the following error message:
java.security.InvalidKeyException: No installed provider supports this key: sun.security.rsa.RSAPublicKeyImpl
at javax.crypto.Cipher.chooseProvider(Cipher.java:892)
at javax.crypto.Cipher.init(Cipher.java:1724)
~~~~
If anyone has any suggestions, or if I need to provide more information please let me know. I am fairly stuck right now and I am at this point debating writing a script to simply run the openssl command and run that script from java...
Conclusion
After reading through #dave-thompson-085's answer I realized that there was a really good reason why I could not find what I was wanting to do. So therefore I decided to go ahead and just call the openssl process from java using a process builder. I was able to recreate the openssl command from above as a Process in java, start it and run it with the following code:
File cert = new File(getClass().getClassLoader().getResource("public.crt").getFile());
ProcessBuilder openSslBuilder = new ProcessBuilder("openssl", "smime", "-encrypt", "-binary",
"-aes-256-cbc", "-in", "C:\\volatile\\generic.tar.gz", "-out",
"C:\\volatile\\generic.tar.gz.enc", "-outform", "DER", cert.getPath());
Process openssl = openSslBuilder.start();
openssl.waitFor();
System.out.println(openssl.exitValue());
openssl.destroy();
Hopefully this helps someone else who is looking to attempt this as well and maybe save someone a bunch of time!
First, to be clear: the openssl smime command actually handles both S/MIME and CMS (aka PKCS7) formats; these are related but different standards that basically use different file formats for essentially the same cryptographic operations. With -outform DER you are actually doing CMS/PKCS7.
Second and more fundamental: CMS/PKCS7, and S/MIME, and most other common cryptographic schemes like PGP, actually does hybrid encryption. Your data is not actually encrypted with RSA; instead your data is encrypted with a symmetric algorithm (here AES-256-CBC, since you selected that) using a randomly generated key called the DEK (data encryption key) and the DEK is encrypted with RSA using the recipient's publickey (obtained from their certificate), and both of those results plus a good deal of metadata is arranged into a fairly complicated data structure. The recipient can parse the message to extract these pieces, then use RSA with their privatekey to decrypt the DEK, then AES-decrypt the data with the DEK. Note you always use RSA keys for RSA, and AES keys for AES; symmetric keys are pretty much all just bits and only vary in size, but public-key cryptographic keys including RSA (also DH, DSA, ECC and more) are much more complicated and cannot be intermixed.
Trying to encrypt data directly with RSA as you did, in addition to being wrong, won't work in general because RSA can only encrypt limited amounts of data, depending on the key size used, typically about 100-200 bytes. Symmetric encryption also has some limits, but they are generally much larger; AES-CBC is good for about 250,000,000,000,000,000 bytes.
If you want to implement this yourself, you need to read the standard for CMS particularly the section on EnvelopedData using KeyTransRecipientInfo (for RSA), combined with the rules for ASN.1 BER/DER encoding. This is not a simple job, although it can be done if you want to put the effort in.
If you can use a third-party library in Java, the 'bcpkix' jar from https://www.bouncycastle.org has routines that support CMS, among several other things. This is usually easy if you are writing a program to run yourself, or in your department. If this is to be delivered to outside users or customers who may not like having to manage a dependency, maybe not.
That said, running another program to do something isn't necessarily bad practice in my book, and can be done directly from java (no script). Unless you (need to) do it very often, such as 100 times a second.
I am trying to encrypt a string with php open_ssl and then decrypt it with Java. I thought I kind of understood what was going on, but apparently not.
At first I was unable to get the algorithms to match up. From what I can gather, openssl_private_encrypt() is using RSA and although the documentation is about PKCS1_PADDING, from what I read it seems that it was changed to use PKCS5/7 to become more secure. And I cannot get any Java cipher with RSA/NONE/PKCS5 or PKCS7.
But I thought I was having success by using NoPadding, and filling the block myself. This is with an existing 512 bit key that I converted from DER to PEM with openssl. I had a test string of
0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
I was able to read in the private key in php and encrypt the text with
$fp=fopen("/folder/private_key.pem","r");
$privkey_res=fread($fp,1024);
$privkey = openssl_pkey_get_private($privkey_res);
$padding = OPENSSL_NO_PADDING;
openssl_private_encrypt($texttocrypt, $encryptedtext, $privkey, $padding);
file_put_contents("/folder/encrypted.txt", $encryptedtext );
Then back in Java I then was able to correctly decrypt that string using
Cipher cipherb = Cipher.getInstance("RSA/NONE/NoPadding");
cipherb.init(Cipher.DECRYPT_MODE, publicKey);
decrypted = cipherb.doFinal(text.getBytes());
So I thought I could get things working to be useful. However, then I changed the test string slightly, like the last 'f' to 'g'
0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdeg
And then I got complete garbage on the decryption. Although no error. And if I just changed the first character, it still decrypted correctly.
At this point I am not even sure what it is that I don't understand. But is there a way to do my original goal? Encrypt with php open_ssl and decrypt with Java.
Thanks
i try to find the good way for the best technology/method for hidden password in a file, but without use external jar or library.
Actually i use one object that represent a list of user name and password. Convert my list in a xml (only in memory) and after that, i store in a file with AES.
Use only java 7, no external library.
Is a good/secure method?
If this operation is no good, is possible to create dynamically xml encrypted?
thanks
You can use a FileOutputStream wrapped in a CipherOutputStream.
It's not really secure to save passwords encrypted with AES because:
1) Where do you store the key? If you store it in the server, if an attacker violates the server and finds the key, he will have complete acces to the users information.
2) Do you really need to know the users' passwords? In many application, for security reasons, it's better to keep only the hash of the password. The username can be stored in plaintext and you can also add a salt to the password to enforce it. You can do that with some algorithms offered by Java7 platform. In this way, even if someone enters your server, he can't use users login informations without breaking the hash function.
Here's an example that worked for me:
public byte[] getHash(String password, byte[] salt, String algorithm) throws NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest digest = MessageDigest.getInstance(algorithm);//The String rapresents the alg you want to use: for example "SHA-1" or "SHA-256"
digest.reset();
digest.update(salt);
return digest.digest(password.getBytes("UTF-8"));
}
You can also look at this link for a more complete example: https://www.owasp.org/index.php/Hashing_Java