5

I'm attempting to generate an MD5 hash in my Apex code and having issues converting the Blob object that Crypto.generateDigest() returns back into a string. The input Blob is a just-Blob.valueOf()-converted basic String, so there's no apostrophes or special characters to worry about here.

The following code shows what I'm trying to do, and I don't understand why the result is not an acceptable String. I am running the following in an "Execute Anonymous" window, it runs fine until it hits the last line md5hash.toString() -- the exact error I receive is "Line: 7, Column: 1 System.StringException: BLOB is not a valid UTF-8 string"

String myString = 'Some String';
system.debug(myString);
Blob myBlob = Blob.valueOf(myString);
system.debug(myBlob);
Blob md5hash = Crypto.generateDigest('MD5', myBlob);
System.debug(md5hash);
System.debug(md5hash.toString());

Here's a PHP equivalent that shows the sort of String I'm wanting to see:

php > var_dump(md5('Some String'));
string(32) "83beb8c4fa4596c8f7b565d390f494e2"

Here's the log file showing output I receive when I comment-out the offensive last line:

43.0 APEX_CODE,DEBUG;APEX_PROFILING,INFO;CALLOUT,INFO;DB,INFO;NBA,INFO;SYSTEM,DEBUG;VALIDATION,DEBUG;VISUALFORCE,INFO;WAVE,INFO;WORKFLOW,INFO
Execute Anonymous:     String myString = 'Some String';
Execute Anonymous:     system.debug(myString);
Execute Anonymous:     Blob myBlob = Blob.valueOf(myString);
Execute Anonymous:     system.debug(myBlob);
Execute Anonymous:     Blob md5hash = Crypto.generateDigest('MD5', myBlob);
Execute Anonymous:     System.debug(md5hash);
Execute Anonymous: //    System.debug(md5hash.toString());
[redacted]
11:56:17.1 (1355526)|EXECUTION_STARTED
11:56:17.1 (1360390)|CODE_UNIT_STARTED|[EXTERNAL]|execute_anonymous_apex
11:56:17.1 (1914766)|USER_DEBUG|[2]|DEBUG|Some String
11:56:17.1 (1989307)|USER_DEBUG|[4]|DEBUG|Blob[11]
11:56:17.1 (2029986)|SYSTEM_MODE_ENTER|false
11:56:17.1 (2198986)|SYSTEM_MODE_EXIT|false
11:56:17.1 (2225737)|USER_DEBUG|[6]|DEBUG|Blob[16]
11:56:17.2 (2265865)|CUMULATIVE_LIMIT_USAGE
11:56:17.2 (2265865)|LIMIT_USAGE_FOR_NS|(default)|
  Number of SOQL queries: 0 out of 100
  Number of query rows: 0 out of 50000
  Number of SOSL queries: 0 out of 20
  Number of DML statements: 0 out of 150
  Number of DML rows: 0 out of 10000
  Maximum CPU time: 0 out of 10000
  Maximum heap size: 0 out of 6000000
  Number of callouts: 0 out of 100
  Number of Email Invocations: 0 out of 10
  Number of future calls: 0 out of 50
  Number of queueable jobs added to the queue: 0 out of 50
  Number of Mobile Apex push calls: 0 out of 10

11:56:17.2 (2265865)|CUMULATIVE_LIMIT_USAGE_END

11:56:17.1 (2300166)|CODE_UNIT_FINISHED|execute_anonymous_apex
11:56:17.1 (3422553)|EXECUTION_FINISHED

To be clear, this is not an attempt to reverse a given MD5 hash. I just want to see what the hashed value itself is, and it should be a String.

8

Instead of toString, you need to actually use EncodingUtil.convertToHex to get the output you expect:

String myString = 'Some String';
system.debug(myString);
Blob myBlob = Blob.valueOf(myString);
system.debug(myBlob);
Blob md5hash = Crypto.generateDigest('MD5', myBlob);
System.debug(md5hash);
System.debug(EncodingUtil.convertToHex(md5hash));

Blob#toString ties to convert the binary data directly into a UTF-8 compatible string (which would be 16 characters long). However, since one or more bytes are likely to be illegal UTF-8 characters, you end up getting this error.

The EncodingUtil.convertToHex method, by contrast, converts the binary data into a format where each byte is converted to 2 bytes, using values from 0-9 and a-f, the hexadecimal numbering system. This is the same format that all other major users of md5 and crypto in general would expect to receive.

2

This is a useful cryptography helper class I often use:

public class CryptoUtils {

    public static String SHA1Hash(String value) {
        return hash('SHA1', value);
    }

    public static String MD5Hash(String value) {
        return hash('MD5', value);
    }

    public static String SHA256Hash(String value) {
        return hash('SHA-256', value);
    }

    public static String SHA512Hash(String value) {
        return hash('SHA-512', value);
    }

    private static String hash(String algorithmName, String input) {
        return EncodingUtil.convertToHex(Crypto.generateDigest(algorithmName, Blob.valueOf(input)));
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.