实现MD5,追加位1和0

时间:2011-08-14 10:31:04

标签: java algorithm md5 implementation

所以我不确定我是否正确地这样做了。我在这里使用伪代码:http://en.wikipedia.org/wiki/MD5

它说:

//Pre-processing:
append "1" bit to message
append "0" bits until message length in bits ≡ 448 (mod 512)
append length to message

在java中,我能否将消息转换为字节数组。然后通过获取字符串长度* 8来获取位数。然后执行类似448 - ((#bits+1) mod 512)的操作以获取要追加的0位数。

然后将该字节数组复制到另一个数组,但用0填充第一个字节,然后填充1。

示例:

字符串是746位 然后我会做448 - ((746+1) mod 512) = 213

所以我需要用213“0”位然后1“1”位填充字符串。

那么我的数组就像是

byteArr[0] = 0x00
byteArr[1] = 0x00
...
byteArr[27] = 000001(Rest of message bits)
byteArr[n] = Rest of the bytes from message

我怎样才能找到1的位置?基本上,如果我追加1位,我怎么能知道它是否会产生短路。

是否有更简单的方法或其他方式来做到这一点?

5 个答案:

答案 0 :(得分:2)

这些位必须附加到消息的 end ,而不是开头。在将消息填充到448位mod 512之后,您必须附加消息的长度(没有填充位)。 但只要这不是练习,你应该使用JB Nizet提到的算法。

答案 1 :(得分:2)

当其他人抓到时,“附加”意味着添加到最后。所以你真正希望得到的是一个0x80的字节,在消息之后是一堆0字节,直到总字节数为8,小于64的倍数。

答案 2 :(得分:0)

有一种更简单的方法:只需使用Java SE API为您提供MD5 algorithm already implemented

旁注:Java中的String包含字符,而不是字节。不要使用术语“字符串”来表示二进制消息。使用术语字节数组。

答案 3 :(得分:0)

如果你实施加密算法,你应该把它作为一个好习惯,将官方规范作为实现的蓝图。和维基百科一样,我仍然不会依赖它来实现安全关键算法。当然,除了官方规范之外,还可以使用它。所以我从RFC 1321开始,只会从其他来源获得“灵感”。

RFC 1321甚至包含C中的实现,你也可以检查它。关于填充步骤,他们说

  

3.1步骤1.附加填充位

     

消息被“填充”(扩展),以便其长度(以位为单位)      全等到448,模512.也就是说,消息是扩展的      它只是64位,是512位长的倍数。      即使消息的长度为,也始终执行填充      已经与448一致,模512。

     

填充如下执行:将单个“1”位附加到      消息,然后附加“0”位,以便以位为单位的长度      填充的消息变为全等448,模512.总之,在      附加至少一位且最多512位。

这意味着你必须首先附加1,然后用剩余的零填充,而不是反过来。填充的其余位(包括“1”)将始终是8的倍数,因为您的输入是字节,其位长也是8的倍数。因此,假设您必须填充128位。这意味着1加127 0位。总共,这是16(16 * 8 = 128)字节的填充,其中第一个字节的最高位设置为1,即第一个字节将变为0x80,其余为0x00。所以这意味着您可以将此填充步骤简化为

  • n =要填充的总字节数=以位为单位的余数除以8
  • 将0x80附加到您的邮件
  • 在消息
  • 上附加n-1次0x00

答案 4 :(得分:-2)

你所说的是腌制哈希。

我就是这样做的。

public static byte[] getSecure8ByteSalt(){
    SecureRandom random = null;
    try {
        random = SecureRandom.getInstance("SHA1PRNG");
        byte [] bSalt =  new byte[8];
        random.nextBytes(bSalt);
        return bSalt;
    } catch (NoSuchAlgorithmException e) {
        log.error(e.getMessage(),e);
    }
    return new byte[]{
         (byte)0xA9, (byte)0x9B, (byte)0xC8, (byte)0x32,
         (byte)0x56, (byte)0x34, (byte)0xE3, (byte)0x03
     };
}

调用salt的方法称为hash:

private void hash(String passwd, int hashType){
    byte[] bSalt = new byte[8];
    try {
        if(this.salt == null){
            bSalt = getSecure8ByteSalt();
        }
        else{ 
            bSalt = base64ToByte(salt);
        }
    } catch (IOException e1) {
        log.error(e1.getMessage(),e1);
        return;
    }


    byte[] bDigest=null;
    try {
        bDigest = getHash(ITERATION_NUMBER,passwd,bSalt,hashType);
    } catch (NoSuchAlgorithmException e) {
        log.error(e.getMessage(),e);
    }

    String sDigest = byteToBase64(bDigest);
    if(this.salt == null)
        setSalt(byteToBase64(bSalt));
setPasswordHash(sDigest);
}

Byte to base 64方法:

public static byte[] base64ToByte(String data) throws IOException {
    BASE64Decoder decoder = new BASE64Decoder();
    return decoder.decodeBuffer(data);
}

public static String byteToBase64(byte[] data){
       BASE64Encoder endecoder = new BASE64Encoder();
       return endecoder.encode(data);
}

getHash方法:

    private byte[] getHash(int iterationNb, String password, byte[] salt, int hashType) throws NoSuchAlgorithmException {
       MessageDigest digest = MessageDigest.getInstance(HASH_TYPE[hashType]);
       digest.reset();
       digest.update(salt);
       byte[] input = null;
        try {
            input = digest.digest(password.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            log.error(e.getMessage(),e);
        }
       for (int i = 0; i < iterationNb; i++) {
           digest.reset();
           input = digest.digest(input);
       }
       return input;
   }