7

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.

2 Answers 2

12

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.

1
3

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)));
    }
}

You must log in to answer this question.

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