How to SHA1 hash a string in Android?

109,922

Solution 1

You don't need andorid for this. You can just do it in simple java.

Have you tried a simple java example and see if this returns the right sha1.

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class AeSimpleSHA1 {
    private static String convertToHex(byte[] data) {
        StringBuilder buf = new StringBuilder();
        for (byte b : data) {
            int halfbyte = (b >>> 4) & 0x0F;
            int two_halfs = 0;
            do {
                buf.append((0 <= halfbyte) && (halfbyte <= 9) ? (char) ('0' + halfbyte) : (char) ('a' + (halfbyte - 10)));
                halfbyte = b & 0x0F;
            } while (two_halfs++ < 1);
        }
        return buf.toString();
    }

    public static String SHA1(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        MessageDigest md = MessageDigest.getInstance("SHA-1");
        byte[] textBytes = text.getBytes("iso-8859-1");
        md.update(textBytes, 0, textBytes.length);
        byte[] sha1hash = md.digest();
        return convertToHex(sha1hash);
    }
}

Also share what your expected sha1 should be. Maybe ObjectC is doing it wrong.

Solution 2

A simpler SHA-1 method: (updated from the commenter's suggestions, also using a massively more efficient byte->string algorithm)

String sha1Hash( String toHash )
{
    String hash = null;
    try
    {
        MessageDigest digest = MessageDigest.getInstance( "SHA-1" );
        byte[] bytes = toHash.getBytes("UTF-8");
        digest.update(bytes, 0, bytes.length);
        bytes = digest.digest();

        // This is ~55x faster than looping and String.formating()
        hash = bytesToHex( bytes );
    }
    catch( NoSuchAlgorithmException e )
    {
        e.printStackTrace();
    }
    catch( UnsupportedEncodingException e )
    {
        e.printStackTrace();
    }
    return hash;
}

// http://stackoverflow.com/questions/9655181/convert-from-byte-array-to-hex-string-in-java
final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHex( byte[] bytes )
{
    char[] hexChars = new char[ bytes.length * 2 ];
    for( int j = 0; j < bytes.length; j++ )
    {
        int v = bytes[ j ] & 0xFF;
        hexChars[ j * 2 ] = hexArray[ v >>> 4 ];
        hexChars[ j * 2 + 1 ] = hexArray[ v & 0x0F ];
    }
    return new String( hexChars );
}

Solution 3

If you can get away with using Guava it is by far the simplest way to do it, and you don't have to reinvent the wheel:

final HashCode hashCode = Hashing.sha1().hashString(yourValue, Charset.defaultCharset());

You can then take the hashed value and get it as a byte[], as an int, or as a long.

No wrapping in a try catch, no shenanigans. And if you decide you want to use something other than SHA-1, Guava also supports sha256, sha 512, and a few I had never even heard about like adler32 and murmur3.

Solution 4

final MessageDigest digest = MessageDigest.getInstance("SHA-1");
result = digest.digest(stringToHash.getBytes("UTF-8"));

// Another way to construct HEX, my previous post was only the method like your solution
StringBuilder sb = new StringBuilder();

for (byte b : result) // This is your byte[] result..
{
    sb.append(String.format("%02X", b));
}

String messageDigest = sb.toString();

Solution 5

Totally based on @Whymarrh's answer, this is my implementation, tested and working fine, no dependencies:

public static String getSha1Hex(String clearString)
{
    try
    {
        MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
        messageDigest.update(clearString.getBytes("UTF-8"));
        byte[] bytes = messageDigest.digest();
        StringBuilder buffer = new StringBuilder();
        for (byte b : bytes)
        {
            buffer.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
        }
        return buffer.toString();
    }
    catch (Exception ignored)
    {
        ignored.printStackTrace();
        return null;
    }
}
Share:
109,922
Martin
Author by

Martin

Updated on July 05, 2022

Comments

  • Martin
    Martin almost 2 years

    In Objective C I've been using the following code to hash a string:

    -(NSString *) sha1:(NSString*)stringToHash {    
        const char *cStr = [stringToHash UTF8String];
        unsigned char result[20];
        CC_SHA1( cStr, strlen(cStr), result );
        return [NSString stringWithFormat:@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
            result[0], result[1], result[2], result[3], 
            result[4], result[5], result[6], result[7],
            result[8], result[9], result[10], result[11],
            result[12], result[13], result[14], result[15],
            result[16], result[17], result[18], result[19]
            ];  
    }
    

    Now I need the same for Android but can't find out how to do it. I've been looking for example at this: Make SHA1 encryption on Android? but that doesn't give me the same result as on iPhone. Can anyone point me in the right direction?

  • Martin
    Martin about 13 years
    Thanks for this answer! The convertToHex() I was using was doing it wrong. Your code is working fine.
  • Peter
    Peter almost 13 years
    Greate .. Thank you very much
  • Melvin
    Melvin over 11 years
    if the sha1 string is supposed to start with a 0, then the 0 is left out. So this method returns a wrong sha-1 encoded string in some cases.
  • NicoMinsk
    NicoMinsk over 11 years
    WARNING : not work if you want to hash '1122', it's start with 0
  • Adam
    Adam over 11 years
    Correct! Updated the answer to deal with this. Thanks for catching this guys.
  • eselk
    eselk over 11 years
    As the code is currently it just returns a hex version of original string, no hashing. Need to call digest.digest() somewhere.
  • CodesInChaos
    CodesInChaos over 11 years
    Why iso-8559-1 encoding instead of UTF-8? These is little reason to use such legacy encodings unless you need to be compatible with some existing application.
  • CodesInChaos
    CodesInChaos over 11 years
    Using default encoding(somestring.GetBytes()) will give you platform dependent hashes. Not nice. Use a fixed encoding, preferably UTF-8.
  • Max Steinmeyer
    Max Steinmeyer over 11 years
    As someone mentioned below, text.length() isn't correct, as it won't return the number of bytes. You need to use the length of the array returned by getBytes. And, yeah, it really probably shouldn't be iso-8859.
  • Triang3l
    Triang3l about 11 years
    The point was not to show how to format the hash, the point was to show how to create it.
  • JM Lord
    JM Lord about 9 years
    Note: use "%02x" if you want to avoid capitalization.
  • cprcrack
    cprcrack over 8 years
    Awesome, I'm sharing my complete implementation: stackoverflow.com/a/33260623/423171
  • Ridcully
    Ridcully over 8 years
    As suggested for other answers, you should use clearString.getBytes("UTF-8") to be on the safe side.
  • Muz
    Muz about 8 years
    I really like this answer because it fits into Android so well, and doesn't need me to double check SO code for security holes :p
  • android developer
    android developer over 7 years
    Can "md" ever be null on Android ?
  • Carlos Espinoza
    Carlos Espinoza about 7 years
    I implemented this code into my Android App and works perfect. Thank for share.