Android의 MD5 해싱


91

간단한 C # HTTP 리스너와 '대화'해야하는 간단한 안드로이드 클라이언트가 있습니다. POST 요청에서 사용자 이름 / 암호를 전달하여 기본 수준의 인증을 제공하고 싶습니다.

MD5 해싱은 C #에서 사소하고 내 요구에 충분한 보안을 제공하지만 Android 쪽에서이 작업을 수행하는 방법을 찾을 수없는 것 같습니다.

편집 : MD5 약점에 대해 제기 된 문제를 해결하기 위해 C # 서버는 내 안드로이드 클라이언트 사용자의 PC에서 실행됩니다. 대부분의 경우 그들은 자신의 LAN에서 Wi-Fi를 사용하여 서버에 액세스하지만 자신의 책임하에 인터넷에서 액세스하도록 선택할 수 있습니다. 또한 서버의 서비스는 내가 제어 할 수없는 타사 응용 프로그램에 대한 MD5 패스 스루를 사용해야합니다.


6
MD5를 사용하지 마십시오. SHA512를 사용합니다.
SLaks 2011 년

1
왜? SHA512는 MD5보다 어렵지 않습니다. 지금부터 5 년 후 MD5를 사용하는 레거시 클라이언트에 갇히고 싶지는 않습니다.
SLaks 2011 년

2
프로토콜에서 임시 값을 사용하여 리플레이 공격을 차단할 수 있기를 바랍니다.
sarnold

1
@NickJohnson : 질문에 답하기 위해 의도적으로 약한 옵션을 선택하는 이유는 무엇입니까? 또 다른 질문으로 ... 16 개월 전에 게시 한 질문에 대해 댓글을 달 필요가 있다고 생각하는 이유는 무엇입니까? 그러나 정말로 알고 싶다면 (당신이 당신의 SLaks에 대한 주석을 보면) 알파 단계 코드 였고 PC 끝 (내가 작성하지 않음)은 MD5 해싱을 사용했습니다. 요구 사항은 기본적으로 추가 복잡성없이 통과 시나리오에 대한 것이 었습니다. 그 당시 위험을 알고있는 약 10 명의 알파 단계 테스터가있었습니다. 내가 질문 한 이후로 더 복잡한 보안이 통합되었습니다.
Squonk

1
...뭐? 아니, 그것은 단지 잘못된 것이 아니라 위험 할 정도로 잘못된 것입니다.
Nick Johnson

답변:


231

여기에 - 당신이 사용할 수있는 구현입니다 (날짜 Java 규약에 더 많은 최대 사용하도록 업데이트 for:each, 루프를 StringBuilder대신 StringBuffer:)

public static String md5(final String s) {
    final String MD5 = "MD5";
    try {
        // Create MD5 Hash
        MessageDigest digest = java.security.MessageDigest
                .getInstance(MD5);
        digest.update(s.getBytes());
        byte messageDigest[] = digest.digest();

        // Create Hex String
        StringBuilder hexString = new StringBuilder();
        for (byte aMessageDigest : messageDigest) {
            String h = Integer.toHexString(0xFF & aMessageDigest);
            while (h.length() < 2)
                h = "0" + h;
            hexString.append(h);
        }
        return hexString.toString();

    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return "";
}

기본 보안 수준 (MD5 는 손상된 것으로 간주되고 쉽게 악용 될 수 있음 ) 까지 포함하는 시스템에는 권장되지 않지만 때로는 기본 작업에 충분합니다.


트릭을 해주셔서 감사합니다. 이 단계에서 MD5가 충분한 이유에 대한 내 편집을 참조하십시오.
Squonk 2011 년

6
아래에 더 정확한 답변이 표시되도록 IMO에 투표하십시오.
Adam

4
0은 아래 답변과 같이 음식을 제공하지 않습니다.
SohailAziz

최신 자바 표준 (for : each, StringBuilder)을 사용하도록 업데이트 됨
loeschg 2014 년

3
MD5가 구현되지 않은 Android 운영 체제가 NoSuchAlgorithmException있습니까?
VSB

50

받아 들인 대답은 Android 2.2에서 작동하지 않았습니다. 이유는 모르겠지만 제로 (0)의 일부를 "먹고"있었습니다. Apache commons 는 Android 2.3.x부터 만 지원되는 메서드를 사용하기 때문에 Android 2.2에서도 작동하지 않았습니다. 또한 MD5 문자열을 원한다면 Apache commons는 너무 복잡합니다. 작은 기능 만 사용하기 위해 전체 라이브러리를 유지해야하는 이유 ...

마지막으로 여기 에서 완벽하게 작동 하는 다음 코드 스 니펫을 찾았 습니다. 누군가에게 도움이 되길 바랍니다 ...

public String MD5(String md5) {
   try {
        java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
        byte[] array = md.digest(md5.getBytes("UTF-8"));
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < array.length; ++i) {
          sb.append(Integer.toHexString((array[i] & 0xFF) | 0x100).substring(1,3));
       }
        return sb.toString();
    } catch (java.security.NoSuchAlgorithmException e) {
    } catch(UnsupportedEncodingException ex){
    }
    return null;
}

4
나를 위해 일했습니다. 내가 변경 한 md5.getBytes를 ( "UTF-8") . 나는 그것을 확인했습니다 q4m'x68n6_YDB4ty8VC4 &} wqBtn ^ 68W 위의 코드와 함께, 결과는 0c70bb931f03b75af1591f261eb77d0b 아니, c70bb931f03b75af1591f261eb77d0b . 0이 제자리에 있습니다
Inoy 2014

28

androidsnippets.com 코드는 결과 해시에서 0이 잘린 것처럼 보이기 때문에 안정적으로 작동하지 않습니다.

더 나은 구현이 여기에 있습니다 .

public static String MD5_Hash(String s) {
    MessageDigest m = null;

    try {
            m = MessageDigest.getInstance("MD5");
    } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
    }

    m.update(s.getBytes(),0,s.length());
    String hash = new BigInteger(1, m.digest()).toString(16);
    return hash;
}

이 경우 Android에서 "m"이 null 일 수 있습니까?
안드로이드 개발자

3
@androiddeveloper 당신이 완전히 옳습니다. catch 블록은 여기서 제대로 구현되지 않았으며 return 문이 있어야합니다. 그렇지 않으면 m.update가 호출 될 때 null 참조 오류가 발생합니다. 또한 확실하지 않지만 개별 바이트에서 0을 제거하지 않을 수 있지만 전체 32 자 해시에서 선행 0을 제거 할 수 있다고 생각합니다. toString (16) 대신 String.Format ( "% 032X", bigInt)가 의도 한대로 작동한다고 생각합니다. 또한 16 진수를 대문자로 할 것인지 소문자로 할 것인지 선택할 수 있습니다 (소문자는 "% 032x").
rsimp

1
이 버전은 결함이 있습니다. MD5가 "0"으로 시작하면 생성 된 MD5는 앞에 0이 없습니다.이 솔루션을 사용하지 마십시오.
elcuco

20

Apache Commons Codec을 사용하는 것이 옵션 인 경우 구현이 더 짧습니다.

String md5Hex = new String(Hex.encodeHex(DigestUtils.md5(data)));

또는 SHA :

String shaHex= new String(Hex.encodeHex(DigestUtils.sha("textToHash")));

위의 출처 .

링크를 따라 그의 솔루션을 찬성하여 올바른 사람을 선정하십시오.


Maven 저장소 링크 : https://mvnrepository.com/artifact/commons-codec/commons-codec

현재 Maven 종속성 (2016 년 7 월 6 일 기준) :

<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.10</version>
</dependency>

1
표준 라이브러리에 이미 존재하는 API에 대해 외부 라이브러리를 사용하는 이유는 무엇입니까?
m0skit0

12

DigestUtils를 사용하는 위의 솔루션은 저에게 효과적이지 않았습니다. 내 버전의 Apache commons (2013 년 최신 버전)에는 그러한 클래스가 없습니다.

여기 한 블로그에서 또 다른 해결책을 찾았습니다 . 완벽하게 작동하며 Apache commons가 필요하지 않습니다. 위의 답변에 나온 코드보다 약간 짧아 보입니다.

public static String getMd5Hash(String input) {
    try {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] messageDigest = md.digest(input.getBytes());
        BigInteger number = new BigInteger(1, messageDigest);
        String md5 = number.toString(16);

        while (md5.length() < 32)
            md5 = "0" + md5;

        return md5;
    } catch (NoSuchAlgorithmException e) {
        Log.e("MD5", e.getLocalizedMessage());
        return null;
    }
}

다음 가져 오기가 필요합니다.

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

MD5 해시 길이가 32 자 이하임을 아는 것이 중요합니다. 감사합니다!
Pelanes 2014

3
마지막 몇 줄은 다음으로 대체 할 수 있습니다. return String.Format ( "% 032X", number); 그렇지 않으면 나는이 대답을 정말 좋아합니다.
rsimp

10

이것은 위의 Andranik 및 Den Delimarsky 답변의 약간 변형이지만 조금 더 간결하고 비트 논리가 필요하지 않습니다. 대신 내장 String.format메서드를 사용하여 바이트를 두 문자 16 진수 문자열로 변환합니다 (0을 제거하지 않음). 일반적으로 나는 그들의 대답에 대해 논평을했지만 그렇게 할 명성이 없습니다.

public static String md5(String input) {
    try {
        MessageDigest md = MessageDigest.getInstance("MD5");

        StringBuilder hexString = new StringBuilder();
        for (byte digestByte : md.digest(input.getBytes()))
            hexString.append(String.format("%02X", digestByte));

        return hexString.toString();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
        return null;
    }
}

대신 소문자 문자열을 반환 %02X하려면 %02x.

편집 : wzbozon의 답변과 같이 BigInteger를 사용하면 답변을 더욱 간결하게 만들 수 있습니다.

public static String md5(String input) {
    try {
        MessageDigest md = MessageDigest.getInstance("MD5");
        BigInteger md5Data = new BigInteger(1, md.digest(input.getBytes()));
        return String.Format("%032X", md5Data);
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
        return null;
    }
}

4

Kotlin에서 간단한 라이브러리를 만들었습니다.

루트 build.gradle에 추가

allprojects {
        repositories {
            ...
            maven { url 'https://jitpack.io' }
        }
    }

App build.gradle에서

implementation 'com.github.1AboveAll:Hasher:-SNAPSHOT'

용법

Kotlin에서

val ob = Hasher()

그런 다음 hash () 메서드 사용

ob.hash("String_You_Want_To_Encode",Hasher.MD5)

ob.hash("String_You_Want_To_Encode",Hasher.SHA_1)

MD5 및 SHA-1을 각각 반환합니다.

도서관에 대한 추가 정보

https://github.com/ihimanshurawat/Hasher


2

@Andranik 답변의 Kotlin 버전이 있습니다. (기본 문자 집합이 UTF-8이기 때문에 문자 집합 UTF-8을 추가 할 필요가 없음) 로 변경 getBytes하고 array [i]를 정수로 캐스팅해야합니다.toByteArraytoByteArray

fun String.md5(): String? {
    try {
        val md = MessageDigest.getInstance("MD5")
        val array = md.digest(this.toByteArray())
        val sb = StringBuffer()
        for (i in array.indices) {
            sb.append(Integer.toHexString(array[i].toInt() and 0xFF or 0x100).substring(1, 3))
        }
        return sb.toString()
    } catch (e: java.security.NoSuchAlgorithmException) {
    } catch (ex: UnsupportedEncodingException) {
    }
    return null
}

도움이 되었기를 바랍니다.


1

MVC 애플리케이션에서 우리는 long param을 생성합니다.

using System.Security.Cryptography;
using System.Text;
    ...
    public static string getMD5(long id)
    {
        // convert
        string result = (id ^ long.MaxValue).ToString("X") + "-ANY-TEXT";
        using (MD5 md5Hash = MD5.Create())
        {
            // Convert the input string to a byte array and compute the hash. 
            byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(result));

            // Create a new Stringbuilder to collect the bytes and create a string.
            StringBuilder sBuilder = new StringBuilder();
            for (int i = 0; i < data.Length; i++)
                sBuilder.Append(data[i].ToString("x2"));

            // Return the hexadecimal string. 
            result = sBuilder.ToString().ToUpper();
        }

        return result;
    }

Android 애플리케이션에서도 동일합니다 (thenk는 Andranik을 돕습니다).

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
...
public String getIdHash(long id){
    String hash = null;
    long intId = id ^ Long.MAX_VALUE;
    String md5 = String.format("%X-ANY-TEXT", intId);
    try {
        MessageDigest md = java.security.MessageDigest.getInstance("MD5");
        byte[] arr = md.digest(md5.getBytes());
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < arr.length; ++i)
            sb.append(Integer.toHexString((arr[i] & 0xFF) | 0x100).substring(1,3));

        hash = sb.toString();
    } catch (NoSuchAlgorithmException e) {
        Log.e("MD5", e.getMessage());
    }

    return hash.toUpperCase();
}

1

나는 아래 방법을 사용하여 md5를 얻고 싶은 문자열을 전달하여 md5를 제공했습니다.

public static String getMd5Key(String password) {

//        String password = "12131123984335";

        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(password.getBytes());

            byte byteData[] = md.digest();

            //convert the byte to hex format method 1
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < byteData.length; i++) {
                sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1));
            }

            System.out.println("Digest(in hex format):: " + sb.toString());

            //convert the byte to hex format method 2
            StringBuffer hexString = new StringBuffer();
            for (int i = 0; i < byteData.length; i++) {
                String hex = Integer.toHexString(0xff & byteData[i]);
                if (hex.length() == 1) hexString.append('0');
                hexString.append(hex);
            }
            System.out.println("Digest(in hex format):: " + hexString.toString());

            return hexString.toString();

        } catch (Exception e) {
            // TODO: handle exception
        }

        return "";
}

1

SHA-512를 사용하세요. MD5는 안전하지 않습니다.

public static String getSHA512SecurePassword(String passwordToHash) {
    String generatedPassword = null;
    try {
        MessageDigest md = MessageDigest.getInstance("SHA-512");
        md.update("everybreathyoutake".getBytes());
        byte[] bytes = md.digest(passwordToHash.getBytes());
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
        }
        generatedPassword = sb.toString();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return generatedPassword;
}

0

MD5는 약간 오래되었고 SHA-1은 더 나은 알고리즘이며 여기에 예제가 있습니다 .

( 또한 해당 게시물에서 언급했듯이 Java는 Android 특정 코드가 아닌 자체적으로이를 처리합니다. )


4
아니요-2011 년 1 월 (19 개월 전)에이 질문을했을 때 MD5의 대안을 원하지 않았고이 시점에서 제 질문에 응답해야한다고 느낀 이유를 잘 모르겠습니다.
Squonk

21
@Squonk 나는 그것이 stackoverflow의 일반적인 아이디어이기 때문에 응답했습니다. 사실 이후 아무리 시간이 지나도 나중에 질문을 접할 수있는 사람들을 위해 항상 더 나은 답변을 얻으려고 노력하십시오. SHA-1을 제안하는 것에 관해서는 당신이 특별히 SHA-1에 반대했다는 것을 알 수있는 방법이 없었지만 다른 많은 사람들은 그렇지 않을 것입니다. 따라서 이것은 미래에 이것을 발견하고 그들을 지시하는 다른 사람들을 도울 수 있습니다. 보다 현대적인 알고리즘으로.
Adam

3
MD5가 나쁜 선택이지만 SHA1이 좋은 선택이라는 단일 상황을 생각할 수 없습니다. 충돌 저항이 필요하면 SHA1이 아닌 SHA2가 필요합니다. 암호를 해시하는 경우 bcrypt 또는 PBKDF2가 필요합니다. 또는 OP의 경우 적절한 솔루션은 아마도 SSL 일 것입니다.
CodesInChaos 2014-08-04

3
@Adam 이것은 대답이 아닙니다.
Antzi 2016-08-04

0

너무 낭비적인 toHex () 변환은 실제로 다른 제안에서 우세합니다.

private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();

public static String md5string(String s) {
    return toHex(md5plain(s));
}

public static byte[] md5plain(String s) {
    final String MD5 = "MD5";
    try {
        // Create MD5 Hash
        MessageDigest digest = java.security.MessageDigest.getInstance(MD5);
        digest.update(s.getBytes());
        return digest.digest();
    } catch (NoSuchAlgorithmException e) {
        // never happens
        e.printStackTrace();
        return null;
    }
}

public static String toHex(byte[] buf) {
    char[] hexChars = new char[buf.length * 2];
    int v;
    for (int i = 0; i < buf.length; i++) {
        v = buf[i] & 0xFF;
        hexChars[i * 2] = HEX_ARRAY[v >>> 4];
        hexChars[i * 2 + 1] = HEX_ARRAY[v & 0x0F];
    }
    return new String(hexChars);
}

대답은 퀘스트가 MD5에 관한 것일 때 "더 나은"toHex에 대한 것입니다. 따라서 응답이 없습니다. 참고 : Donald Knuth 진짜 문제는 프로그래머가 잘못된 장소와 잘못된 시간에 효율성을 걱정하는 데 너무 많은 시간을 소비했다는 것입니다. 조기 최적화는 프로그래밍의 모든 악 (또는 적어도 대부분)의 근원입니다.
zaph

0

이것은 나를 위해 완벽하게 작동합니다.이를 사용하여 LIST Array에서 MD5를 얻은 다음 JSON 개체로 변환했지만 데이터에만 적용 해야하는 경우. 형식 형식, JsonObject를 귀하의 것으로 대체하십시오.

특히 파이썬 MD5 구현과 불일치하는 경우 이것을 사용하십시오!

private static String md5(List<AccelerationSensor> sensor) {

    Gson gson= new Gson();
    byte[] JsonObject = new byte[0];
    try {
        JsonObject = gson.toJson(sensor).getBytes("UTF-8");
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }

    MessageDigest m = null;

    try {
        m = MessageDigest.getInstance("MD5");
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }

    byte[] thedigest = m.digest(JsonObject);
    String hash = String.format("%032x", new BigInteger(1, thedigest));
    return hash;


}

-1

Scala 언어에 대해 제공된 솔루션 (약간 더 짧음) :

def getMd5(content: Array[Byte]) =
    try {
        val md = MessageDigest.getInstance("MD5")
        val bytes = md.digest(content)
        bytes.map(b => Integer.toHexString((b + 0x100) % 0x100)).mkString
    } catch {
        case ex: Throwable => null
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.