1

I have an urgent business requirement which requires verification of user credentials. We need to encrypt the credentials using sha512 algorithm and then compare it with already encrypted text fields in salesforce.

I went through with the Crypto Class Documentation, but could not find a mechanism to encrypt using SHA512 Algorighm.

Can you please help me out?

On trying to use SHA512, I received an error that this algorithm is not supported for encryption.

The only stuff I could find related to SHA-512 algorithm is below, but I am not sure whether it is encryption or something else?

 Blob digest = Crypto.generateDigest('SHA-512', Blob.valueOf(key));
 String encryptedString = EncodingUtil.base64Encode(digest);
 System.debug('Debug Log for encryptedString:'+encryptedString);
 System.debug('Debug Log for lstDSBDigitalUser[0].oxpassword__c'+lstDSBDigitalUser[0].oxpassword__c);
3
  • 1
    Just a note on terminology here, "encryption" is a reversible process and "hashing" is not. Your case here appears to be about hashing (generating a hash/generating a digest).
    – Derek F
    Jul 8, 2020 at 12:40
  • @Derek, my use case is for encryption only, but as far as SHA-512 algorithm is concerned, I could only find this generateDigest method. I was not sure whether it is used for encryption. Thanks for confirming that. Can you please help with how to carry out such encryption? Jul 8, 2020 at 12:49
  • @Derek, does that mean that hashing could actually work in my use case? Since, I just need to compare the encrypted information with already stored encrypted text? Jul 8, 2020 at 12:52

3 Answers 3

5

There is a difference between hashing (which is what the SHA512 algroithm does) and encrypting.

Hashing

A hash is a one-way function (or it's supposed to be). You put data in, you get a "hash" out, and that process cannot be reversed. Another property of a good hash function is that a small change in input leads to a large change in the output.

This is useful for things like passwords, where it's an extremely bad idea to store the actual data. Instead of storing the password, you store the hash of the password in your database. Then when the user enters their password, you generate the hash for their input (client-side or server-side) and compare that to the stored hash.

Encrypting

Encryption is a reversible process. You put text in, and provide a key, and you get a seemingly random string of text (the ciphertext) out. You can then run the ciphertext through the algorithm again (using the same key if it's "symmetrical" encryption, or a different (but related) key if it's "asymmetrical" encryption) to get the original text.

Encryption is useful when you want to transmit data between two parties (and both parties need to be able to see the original data). Using asymmetric encryption (aka Public-Key Cryptography) can also help validate the source of a piece of data (i.e this data can be decrypted by the public key, so it must have been generated by someone with the corresponding private key. If only 1 person is supposed to have the private key, then you can be reasonably sure that the data came from that person).

Crypto on Salesforce

Salesforce limits the algorithms we can use, and the ones we can use are documented in the Crypto class documentation

For hashing, we can use the MD5 (but don't use it if you can avoid doing so), SHA-1 (also don't), SHA256 and SHA512 functions.

For encrypting, we can use AES-128, AES-192, or AES-256 (more specifically, using the CBC, Cipher-Block Chaining, mode and PKCS#5 padding. We don't have a way to use other modes like GCM (Galois-Counter mode))

AES is a symmetric encryption algorithm (where the challenge is making sure both parties end up with the same key while trying to make sure nobody else knows/has the key). RSA is a family of asymmetric encryption algorithms, and our only real access to that in Salesforce is through Crypto.sign(). The nuances of encryption vs digital signature are beyond our scope here.

For your use case

Hashing and comparing the result sounds like the way to go. I think that current best practice is something like:

  • Use Javascript to generate the hash on the client's hardware (so that the unhashed password is never transmitted over the internet)
  • ...but add a unique-ish "salt" to the password before hashing it (so that if two people have the same password, they don't end up with the same hash. Also helps prevent leaked password hashes from other breaches from being re-used)
  • Send the hash to SFDC (using SSL/TLS, basically just make sure you're using HTTPS, so that nobody else can intercept the hash)
  • Compare the received hash to the one you have stored

That said, if there's a way you can integrate OAuth into this process, that'd likely be preferrable to implementing your own authentication mechanism.

4
  • Thank you so much, @Derek, for such a detailed clarification on this, the use case explained to me seems most suitable to hashing technique. Jul 8, 2020 at 14:33
  • Please note that using SHA512 for password hashing is obsolete. Even with salt. Everything is better with salt, but because hashing is embarrassingly parallel you can (relatively) cheaply brute-force any SHAXXX on GPU (if you are interested to see for yourself, just google "hashcat" - its free and open source program for "attacking" hashes). When you are hashing password, use password hashing function (bcrypt, scrypt, PBKDF2) Jul 9, 2020 at 8:57
  • @Jan'splite'K. Unfortunately, that's not an option on the Salesforce platform. Like I mentioned, we are only given access to a very limited number of algorithms/functions. The platform is also computationally limited (the best I've been able to manage is to find the 925,000th prime or so using the sieve of Eratosthenes, with some improvement possible), so porting, say, PKBDF2, is infeasible. SHA512 is as good as it gets for now.
    – Derek F
    Jul 9, 2020 at 11:19
  • @DerekF Huh... That is... Embarrassing :) But I will leave that comment here for future generations & random googlers. Jul 9, 2020 at 12:23
6

As @DerekF pointed out in his comment, you are conflating Hashing and Encryption. SHA-512 is the 512-bit digest version of the SHA-2 hashing function. Per Wikipedia, a cryptographic hashing function,

is a mathematical algorithm that maps data of arbitrary size (often called the "message") to a bit array of a fixed size (the "hash value", "hash", or "message digest") and is a one-way function, that is, a function which is practically infeasible to invert.

In common practice, passwords are not encrypted, they are just hashed and the hash is stored; when a password needs to be validated, the incoming password is hashed and the result is compared to the stored, hashed password. This is because an encrypted password can be decrypted if you have the key, revealing the plaintext password; but there is no practical way to "unhash" a stored hash.

What this means for you is that the "already encrypted text fields in Salesforce" are just stored hashes; you need to hash your input and compare. The example code you posted was correct should work. For example, if your "already encrypted text field" is Account.password__c:

Account a = [...]; // some account to test
String key = 'TopSecretPassword1000';
Blob digest = Crypto.generateDigest('SHA-512', Blob.valueOf(key));
String encryptedString = EncodingUtil.base64Encode(digest);
if (a.Password__c == encryptedString) {
    // match
}

However, a few notes:

  • You mentioned that the existing fields are Text fields, hence the use of base64Encode to treat the binary hash value (blob) as text. This isn't the only way to encode binary data as text, but it is the most common, and the only method supported in Apex. If the hashed passwords are stored in blob fields, you would skip that step.
  • It is a common practice when hashing passwords for storage to modify the input by adding a random value, called a "salt", before hashing; this value is stored alongside the hashed value and used when testing a candidate password (see the link for much more). If your stored values were salted you'll need the corresponding salt values to test them.
1
  • Thanks @Jason, for your valuable inputs. This is exactly what I want. Jul 8, 2020 at 14:34
2

To expand on the answer from @derek-f (because I don't have enough rep to comment). You should NOT perform the hashing client side.

In the event that the hashes are leaked, a malicious actor could modify the client to simply send the password as it is entered rather than hashing it, at this point the attacker simply pastes the hash itself into the password field to log in without ever having to know the password. If the hashing occurs on the client then the hash IS the password.

The password is protected from eavesdroppers by only ever being sent over encrypted channels, not by pre-hashing.

You should use a salt, but since you are comparing to a pre-existing value that isn't your decision, you have to use the same salt (or no salt) that the original hash was created with.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .