Java method to decrypt an AES string from Angular-Cryptography - java

I'm trying to do a login method that keeps the client password secure and encrypted in my server.
The library I'm using from Angular, is https://github.com/middleout/angular-cryptography
The idea is, to follow these steps:
I set a salt in my module.config:
app.config(['$cryptoProvider', function($cryptoProvider){
$cryptoProvider.setCryptographyKey('thisismysalt');
}]);
I encrypt the password with itself:
user.pw = $crypto.encrypt(user.pw, user.pw);
If I'm registering an user, I re-encrypt the password with itself (repeat step 2) and save it in the DB. If I'm logging, I just send the result from the last step, to the server.
When you decypher the double-encrypted string with the single-encrypted one, you get the single encrypted string again. So if your password was correct, you just compare the result with the single-encrypted string, and you validate the user.
Ok, this method should work (I already did it in Node some time ago), works great with SSL to protect user's passwords even in your server!
But I can't find any library or snippet in Java that can do it. I tried many of them, but they are hard to understand and when I adapt them to my procedure, they just won't work. I tried the following method:
static String IV = "AAAAAAAAAAAAAAAA";
static String plaintext = "test text 123\0\0\0"; /*Note null padding*/
static String encryptionKey = "0123456789abcdef";
public static String decrypt(byte[] cipherText, String encryptionKey) throws Exception{
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
cipher.init(Cipher.DECRYPT_MODE, key,new IvParameterSpec(IV.getBytes("UTF-8")));
return new String(cipher.doFinal(cipherText),"UTF-8");
}
I passed it as first argument, the double-encrypted password from the DB, and as second argument, the single-encrypted password from the frontend:
java.security.InvalidKeyException: Invalid AES key length: 44 bytes
Am I doing something wrong? Should I use a different algorithm?

middleout/angular-cryptography uses CryptoJS 3.1.2 under the hood with the least effort possible.
So
return $crypto.encrypt(plaintext, password);
is the same as
$cryptoProvider.setCryptographyKey(password);
return $crypto.encrypt(plaintext, password);
and the same as
return CryptoJS.AES.encrypt(plaintext, password).toString();
I describe in my answer here how to do the same thing in Java.
If you're using SSL/TLS, there is not much benefit to doing this encryption additionally. The password is already sent in an encrypted way over the internet. Even worse, since the password must be available at the server side, you must store the password in cleartext. That's not how it is done.
You need to use hashing instead with some strong ones being PBKDF2, bcrypt, scrypt and Argon2. Since hash functions are one-way function, you won't be able to "decrypt" the hashes. In order to authenticate your user, you can run the password through the hash function again in order to compare with the hash that is stored in the database. Since you're already using SSL/TLS, the password is already secured during transmission. See more: How to securely hash passwords?

Related

Store password for passing to another service

I wrote an app that queries a Jira API which requires authentication that I provide through Basic Authentication (base64 in the header). The password was stored in the code which has to stop now because I want to hand over the code.
When the users changes their passwords due to the password schedule, the app should prompt the user for the new Jira password, save it securely, and pass it to the Jira API via Basic Authentication.
What's the best way to do this?
Normally, we would hash it but that's not possible because hashing is one-way direction and we need to pass in the real password to Jira instead of a hash.
In case of storing a string which needs to be protected in case of breaches or as a general software data security concern, encryptions should be done. For example, in your case, when the password is taken by the user then it shall be encrypted by the software before storing. While retrieving, the password is decrypted and converted to the hash(or base64) which Jira accepts for the login handshake.
Apart from the simply encrypting and decrypting, a better approach will be to use salts while encrypting and using multiple encryptions in the loop to avoid brute force attempts.
Pseudocode:
unsafe_password = getPasswordFromUser()
salt = getRandomString();
safePassword = encrypt(unsafe_password, salt, key)
// Store the password
putEntryInDB(user, safePassword, salt)
// Retrieve password
[passwordSalt, encryptedPassword] = getSaltAndEncryptedPasswordFromDB()
unsafePassword = decrypt(encryptedPassword, passwordSalt, key)
// Now login into Jira with the actual user's password (unsafePassword)
P.S. You'll be needing to store a key in the code or in some software's configuration.
Source: Attempt 4&5 https://nakedsecurity.sophos.com/2013/11/20/serious-security-how-to-store-your-users-passwords-safely/

How can I encrypt password on client side and decrypt it on server side?

I want to encrypt my password after created user and persist it inside the DB. I write password to "password field" and press "Save button". Then I use this library
For encryption
BasicPasswordEncryptor passwordEncryptor = new BasicPasswordEncryptor();
String encryptedPasword = passwordEncryptor.encryptPassword(myPasword);
user = new User();
user.setUsername(username);
user.setUserRole(role);
user.setFistname(firstname);
user.setLastname(lastname);
user.setGroupId(group);
user.setBssLogin(login);
user.setBssPassword(encryptedPasword);
dao.addCrmUser(user);
After that, I have a new encrypted password inside my DB.
On the server side, I get the user and try to decrypt the password
String login = user.getLogin();
String password = user.getPassword();
String dencryptPassword = encryptor.**NOT_METHOD_FOR_IT**(password);
I need the original password, which is set inside another system.
How can I do this with my current library ?
I fount this and another libraries in Github that use some key but I do not know what is better and/or faster.
You do not decrypt the password from the database to verify it.
You encrypt the password received from the user when trying to login, and compare that encrypted value with the value in the database. Good password encryption is really a hash function, which is not reversible, for security.
The BasicPasswordEncryptor() in jasypt does despite it's name, not encrypt the password, but hashes the password using a one-way-hash (A big tell, is that the method don't require an encryption key).
You can't get the password back after this process, and the password is verified using the checkPassword(String plainPassword, String encryptedPassword) methode.
If you really need to be able to decrypt the password, you need to use another way to do real encryption when encrypting it. This raises the need for handling of encryption keys, which might introduce just as many problems than it sorts (where to store them etc.)

Checking tag and associated data in an AEAD cipher in Java

I need to use AEAD to share information between two users, part of which must be encrypted and part of which should be kept in cleartext.
Is there an API to check the ciphertext tag and access the associated data once a message has been encrypted with AES/GCM ?
In more detail:
I'm using Java 7 with bouncycastle as a provider and I have managed to encrypt and decrypt my data successfully, using the corresponding API:
private byte[] encrypt(SecretKey key, byte[] nonce, byte[] message, byte[] associatedData) throws ... {
Cipher aeadCipher = Cipher.getInstance(AES_GCM_NOPADDING);
aeadCipher.init(Cipher.ENCRYPT_MODE, kint, new GCMParameterSpec(GCM_MAC_SIZE, nonce);
aeadCipher.updateAAD(associatedData);
return aeadCipher.doFinal(message);
}
private byte[] decrypt(SecretKey key, byte[] nonce, byte[] cipherText, byte[] associatedData) throws ... {
Cipher aeadCipher = Cipher.getInstance(AES_GCM_NOPADDING);
aeadCipher.init(Cipher.DECRYPT_MODE, kint, new GCMParameterSpec(GCM_MAC_SIZE, nonce);
aeadCipher.updateAAD(associatedData);
return aeadCipher.doFinal(cipherText);
}
However, it is my understanding that AES/GCM ciphertexts should already contain the parameters that could affect decryption (nonce and associatedData).
Therefore, I would like to be able to retrieve them from the ciphertext, rather than having to store them alongside the ciphertext and pass them along to the decryption function. Furthermore, I'd like to be able to run integrity checks (computing the tag) and run some checks on the associated data without having to completely decrypt the message.
Is there an API that would allow this and that I might have missed ?
So far, I've checked:
The Cipher API: http://docs.oracle.com/javase/7/docs/api/javax/crypto/Cipher.html
This very informative blog post on AES/GCM implementations in Java: http://blog.philippheckel.com/2014/03/01/cipherinputstream-for-aead-modes-is-broken-in-jdk7-gcm/
Since the Java API automatically places the tag at the end, you only have to extract this tag from the your encryption result as follow:
private byte[] getTag(SecretKey key, byte[] nonce, byte[] message, byte[] associatedData) throws ... {
Cipher aeadCipher = Cipher.getInstance(AES_GCM_NOPADDING);
aeadCipher.init(Cipher.ENCRYPT_MODE, kint, new GCMParameterSpec(GCM_MAC_SIZE, nonce);
aeadCipher.updateAAD(associatedData);
byte[] encrypted = aeadCipher.doFinal(message);
// Assuming you have an AAD_SIZE = 128 bits (16 bytes)
return Arrays.copyOfRange (encrypted, encrypted.length-16, encrypted.length)
}
There is no implicit format that stores all the input data of GCM in a specific location. The Java API is already a bit strange in
the sense that it automatically places the tag at the end. This makes the
algorithm more compatible with the Cipher class, but in principle the tag just
needs to be kept with the ciphertext - where does not matter. Now you have the
issue that you don't know where the AAD ends and the ciphertext starts for instance.
So what you can do is either to create your own format (maybe your AAD has a
static size, so you can just concatenate) or you can use a predefined container format.
There is an internet draft
that specifies how to use both modes in the Cryptographic Message Syntax (CMS).
The AAD can then be stored in authenticated atributes, which should also include the required
parameters (including the IV comprising of the nonce).
If you feel masochistic you could also try and use XML-encryption with GCM mode, but beware of the many pitfalls with regard to verifying XML authenticity (e.g. beware that you are actually verifying the data you are going to use).
Bouncy Castle seems to offer support for CMS using GCM.

AES encryption & security flaw

Check update#1
This logic is a candidate for a authentication procedure, done by simple HTTP requests:
I'm sending: userName + encrypted_userName (encrypted_userName is actually the encrypted result of userName, done using AES & as key i use the md5 hash of the password). NOTE: I'm not sending the md5 hashed Password.
on the server I'm comparing: encrypted_userName with own_encrypted_userName (since on server i have access to full info on user, i calculate own encrypted_userName).
Question: is this a security flaw? Say bad guy captures full HTTP request, can he extract password from this 2 infos?
CODE DETAILS, if needed:
private static Cipher getCipher(String key, int mode) throws Exception{
byte[] rawKey = getRawKey(key.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
Key key2 = skeySpec;
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(mode, key2);
return cipher;
}
private static byte[] getRawKey(byte[] seed) throws Exception {
/* BEFORE:
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(seed);
kgen.init(128, sr); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
*/
byte[] raw = MD5Util.getMD5HashRaw(seed);
return raw;
}
(NOTE: reason why i use the hash of the password is that code is compatible among platforms (client is Android device), while the commented version is NOT)
UPDATE#1
Short answer:
Presented logic is not even close to be considered a secure authentication mecanism
(for Why? check Michael's answer bellow)
Decided to use Kerberos (AND not https, since I am not familiar + seems complicated to setup):
It is not a true version of Kerberos (like v4 or v5), it is just my own implementation so lets call it "similar with Kerberos" (I know, I know: DONT "roll your own encryption"!!!),
Here are some details:
it works on UDP (now)
authentication is done only once, by:
client sending a Authenticator message (contains: [userId] in plain text & [something_ecrypted] with [entered_user_password] (curently [something_ecrypted] contains just a timestamp, call it [authenticator_creation_timestamp])) NOTE: password is not transmited
server upon receiving message, tryies to decrypt the [something_ecrypted] with [actual_user_password] -> if SUCCESS then client is who it pretends to be, so i send him back a OK response (as in Kerberos this response contains some stuff, like a [public_key] (a RSA key, but encrypted with user_password) + ticket granting ticket (call it [TGT], encrypted with a password known only by server, currently it doenst expire, this [TGT] also contains some stuff, like these 2 timestamps: [TGT_creation_time_stamp] + [authenticator_creation_timestamp] (the one received in the Authenticator message))
after receiving this OK message, client has procured a valid [public_key].. so nice!
protection agains "reply attack" is not a 100% guarantee, but I see it "safe enought":
on each next HTTP reaquest, i attach as headers these 2 guys [new_request_creation_timestamp] (encrypted with [public_key], procured above) + the [TGT] (untouched, as received above)
on server I just need to validate [new_request_creation_timestamp] agains some math (obvious [TGT] needs to be valid too):
** i expect that the following variables to be almost equal
delta1 = [TGT_creation_time_stamp] - [authenticator_creation_timestamp]
delta2 = now()-[new_request_creation_timestamp]
(I actually allow a difference between them of 5 seconds, but from my tests, its just a matter of some 10-20 millis,
** So initial delta (calculated when creating OK response to Authenticator) should perpetuate on next interactions.
I do find this new approach quite trust-worthy, but if you have an opinion or see a BUG in logic, please share.. Thanks
Yes, this is a weak security mechanism.
Anyone who captures the information sent to the server can easily replay it to authenticate themselves (replay attack).
It is vulnerable to offline password guessing - anyone who captures the information sent to the server can then very quickly test a password list to find what password your user has chosen (by encrypting the observed username using the hash of each password in turn). The hashing can even be precomputed, speeding the attack up further.
Password based authentication protocols should be resistant to replay attacks and offline password guessing attacks.
Simply using an HTTPS (TLS) connection to your server and sending the username and password in plaintext would normally be a better solution.
In response to your update 1:
I strongly advise using HTTPS. It is used everywhere for a reason - it has undergone immense security review and been found to be (largely) secure - far better than what you can get through a SO post.
I haven't considered your updated scheme thoroughly, but as it is based on Kerberos it is also subject to offline password guessing attacks as I described above.
Having successfully authenticated, don't forget about then actually protecting your data - you'll likely need to derive a shared symmetric key then use authentication + encryption on your data...
What i understand is : you are sending Username + Encrypted Username to the server.
Ans:
Since you are sending the Username and the encrypted Username which is : UserName + AES(UserName + MD5 Hashed Password)
If anyone knows or find that you give the Username and also gets the Username from your data to server: No worries. There you stand with AES. If you have doubt in AES encryption check this. Your data is secure.
I don't think this is a security flaw per se because even knowing both the plaintext message and the encrypted one, it is practically impossible to get the AES key. But I still wouldn't recommend to store the passwords hashed with MD5.

How do I RSA encrypt a string with a plaintext key using Java BouncyCastle API on Android

I am trying to encrypt a string using the BouncyCastle API in Android to send off to a server.
I have the public key in plaintext (in memory, not in the filesystem, of course! no need to yell at me, cryptographers ;) ) and I need to use this plaintext public key to encrypt a string to an RSA encrypted string.
This is my class:
public class RSAEncryptor {
//Get certificate from base64 string
public static X509Certificate getCertificateFromBase64String(String string)
throws CertificateException, javax.security.cert.CertificateException
{
if(string.equals("") || string == null) {
return null;
}
byte[] certBytes = Base64.decode(string.getBytes(), Base64.DEFAULT);
X509Certificate cert = X509Certificate.getInstance(certBytes);
return cert;
}
//Get public key from base64 encoded string
public static PublicKey getPublicKeyFromEncodedCertData(String encodedCertData)
throws CertificateException, javax.security.cert.CertificateException
{
if(encodedCertData == null || encodedCertData.equals("")) return null;
X509Certificate cert = getCertificateFromBase64String(encodedCertData);
if(cert == null) return null;
return cert.getPublicKey();
}
public static String rsaEncrypt(String plainText, String keyFromResources)
throws NoSuchAlgorithmException, InvalidKeySpecException,
IOException, NoSuchPaddingException, InvalidKeyException,
IllegalBlockSizeException, BadPaddingException //
{
if(plainText == null || plainText.equals("")) return null;
if(keyFromResources == null || keyFromResources.equals("")) return null;
byte[] encryptedBytes;
Cipher cipher = Cipher.getInstance("RSA");
PublicKey publicKey = null;
try {
publicKey = getPublicKeyFromEncodedCertData(keyFromResources);
}
catch(Exception ex) {
Logger.LogError("getPublicKeyFromEncodedCertData()", ex);
}
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
encryptedBytes = cipher.doFinal(plainText.getBytes());
String encrypted = new String(encryptedBytes);
return encrypted;
}
}
I'm currently not getting the properly encrypted string back out, just a garbled mess like this:
��RB��%����I��Q��F*�bd[#�y�_H]T{KƾuTN�Q�
��U�f��]�S
�q|.t�t�9�Rˇ�����)��{�},ޱ�ª�ǥ#���#k=�WO���f�7t"yP�z�
(The <?>'s are null chars)
I should be getting back an alphanumeric string similar to this:
2+tSXez8JrAIX+VJ2Dy4IsA56XhWpTwF8X2yGGaI6novucXknwykDyqJZICpmYcqx75qBRgxwrW2kY9LmQR2xU17PLqTukAu2Bna8WXYTmJJQ7CWsN3SdABlETRfsYA+g3A2rO2Qp6aR9OCBcFVJpnZJjb9kaOUj5Pcj0tNPFdM= (changed obviously from the actual response :D)
I'd appreciate any help!
Thanks!
Has anyone done this before? I'd love any suggestions you have as to how to fix this.
Problems in the code:
String encrypted = new String(encryptedBytes);
Bad idea! Cipher#doFinal returns a byte[] for a good reason. It looks like random data - turning this into a String will make a mess for sure, because the platform default encoding (UTF-8 in most cases) will interpret the random bytes wrong almost with certainty. So if you want to have a String from encrypted data, then you should Base64- or Hex-encode the byte array.
From what you said you were expecting I would say you want Base64-encoded data, so you should Base64-encode your Cipher's output.
Then, encrypting a string (is this real, human-readable text?) is also less than optimal. Highly vulnerable, the reduced entropy plus the characteristics of the ECB mode (this is used by the RSA cipher) lower the security of your solution drastically.
A plain RSA cipher should never be used to encrypt data that is larger than one block (i.e. larger than the key size of your RSA key) and also only if the data is cryptographically secure random. In 99% of all cases this is only given for symmetric keys used for symmetric Ciphers such as AES etc.
Use RSA for nothing else than symmetric key wrapping and digital signatures, in all remaining cases where you want to actually encrypt sensitive data, you use a symmetric cipher, AES is a good choice - 128 or 256 bits doesn't really matter.
The workflow would look like this:
Generate a symmetric key for AES (16/32 bytes if you use AES-128/256). Now you would RSA-encrypt this symmetric key and nothing else using the server's public key and send the key to the server, then encrypt your private data using AES and the symmetric key, the server would decrypt the symmetric key using its private RSA key and decrypt the packets you send to it.
Use TLS:
Note my use of would. The above is only part of the story. What you just invented there is a Key Transport Protocol. And unless you are designing those for a living chances are high that you won't get this secure on your first try (as in Man-In-The-Middle-Attacks, Reflection Attacks, Replay Attacks, ...).
That's why in my opinion the only widely available secure option to set up a secure channel between client device and server is to use TLS (the former SSL). The protocol was designed specifically for the purpose of exchanging private data with one-way (server only) or two-way authentication (client and server) (authentication is the part where you would use RSA for in your case - configuring the "server certificate").
It has been hardened for years and revised a couple of times to withstand all known attacks on such protocols. And I know, there are messages every other day about how "SSL" has been broken by this or that person, but still, if you set it up carefully it's as secure as it gets for mere mortals without extensive experience in protocol design.
And the nice thing is you only have to configure it on the server (which is quite easy compared to inventing a protocol from scratch) to be able to use fully encrypted, secure communication between both client and server. If you set up a certificate/key pair on the server that was bought from a "public CA" then using TSL is completely transparent to your clients - they merely change their access URL from 'http' to 'https' - the server's certificate will be automatically trusted by being able to identify it in a certificate path that leads to one of the root certificates kept in Java's default trust store cacerts.

Categories

Resources