1. 比如我们有个服务是PHP提供的,要求的签名方式 hmacSha256取摘要,然后 Base64编码转化成可见字符。
  2. PHP那边的源码是这样的
$result = base64_encode(hash_hmac("SHA256" , "内容" , '密钥'));
echo $result;
  • 1.
  • 2.

我们放到php在线调试平台取看看执行结果:

hmac php java结果不一样问题_System

  1. 然后看看Java的
@Test
 void sendMessage2() {
     String  secretKey = "密钥";
     String  apiUser = "";
     String  msisdn = "内容";

     HMac hmac = SecureUtil.hmac(HmacAlgorithm.HmacSHA256, secretKey );

     byte[] digest = hmac.digest(msisdn);

     System.out.println( "code="+ Base64.encodeToString( digest  )  );

 }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

执行结果如下:

hmac php java结果不一样问题_Java_02

  1. 明显Java的比php的结果短了一半
  2. 修改代码看看真实的签名长度
@Test
    void sendMessage2() {
        String  secretKey = "密钥";
        String  apiUser = "";
        String  msisdn = "内容";

        HMac hmac = SecureUtil.hmac(HmacAlgorithm.HmacSHA256, secretKey );

        byte[] digest = hmac.digest(msisdn);


        System.out.println( "code="+ Base64.encodeToString( digest  )  );


        //打印签名长度
        System.out.println("Java签名长度:" + digest.length * 8 );
        System.out.println("php签名长度长度:" + Base64.decode( "MjZiOThhMjE1NDVjMWEzYWNkZGZjMzRjYTM4Mjc3MjYzMTE0NTE4MTIwZjhmMGQ5YTcxYWNmMzhlMDQ1YmRhOA==" ).length*8 );

    }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

执行结果:

hmac php java结果不一样问题_PHP_03

  1. 明显PHP不止256位,但是前面用的算法确实是 sha256?为啥呢?原因是PHP那边用的字符串编码方式是源文件的编码方式。Java 用的unicode码,8个字节表示一个字符,PHP字符串更具源码编码方式不同,可能占用的是16个字节表示一个字符。所以PHP的长了一倍,变成了512位。
  2. 怎么解决其实16字节表示一个8位能表示的字符的时候,高位都是0,如果服务提供方是PHP,那么我们只能Java这边兼容PHP的编码方式。在每个字符的扩展成16字节的字符,高位都补上0。
    下面是处理代码:
/**
  * 一个字节的换成2个字节的(PHP那边的编码方式决定占用2个字节)
  */
 static final String HEXES = "0123456789abcdef";
 public static String getHex( byte [] raw ) {
     if ( raw == null ) {
         return null;
     }
     final StringBuilder hex = new StringBuilder( 2 * raw.length );
     for ( final byte b : raw ) {
         hex.append(HEXES.charAt((b & 0xF0) >> 4))
                 .append(HEXES.charAt((b & 0x0F)));
     }
     return hex.toString();
 }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  1. 最终结果
@Test
    void sendMessage2() {
        String  secretKey = "密钥";
        String  apiUser = "";
        String  msisdn = "内容";

        HMac hmac = SecureUtil.hmac(HmacAlgorithm.HmacSHA256, secretKey );

        byte[] digest = hmac.digest(msisdn);


        //System.out.println( "code="+ Base64.encodeToString( digest  )  );
        //扩展成PHP的一个字符占用16字节
        System.out.println( "code="+ Base64.encodeToString( getHex(digest).getBytes()  )  );


        //打印签名长度
        System.out.println("Java签名长度:" + getHex(digest).getBytes().length * 8 );
        System.out.println("php签名长度长度:" + Base64.decode( "MjZiOThhMjE1NDVjMWEzYWNkZGZjMzRjYTM4Mjc3MjYzMTE0NTE4MTIwZjhmMGQ5YTcxYWNmMzhlMDQ1YmRhOA==" ).length*8 );

    }

    public static String base64sha256(String data, String secret) {
        String hash = null;
        try {
            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
            SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256");
            sha256_HMAC.init(secret_key);
            byte[] res = sha256_HMAC.doFinal(data.getBytes("UTF-8"));

            System.out.println("长度:"  + res.length );
            hash = getHex(res);

            System.out.println("长度:"  + hash.getBytes().length );
            hash = Base64.encodeToString(hash.getBytes("UTF-8"));
        } catch (Exception e){}
        return hash;
    }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.

运行结果结果:

hmac php java结果不一样问题_PHP_04

长度是一样的了,核对签名内容也是一样的,sha256的算法得到256位,强行填充成了,512位,实在没必要。

如果是别人给你提供的服务那么你只能按照别的的规矩来,如果你是PHP服务的提供方,我建议源文件用unicode编码,毕竟sha256算法算出来的摘要的结果本来就应该是256位。

  1. hmac 常见算法 HmacMD5,HmacSHA1,HmacSHA256,HmacSHA384,HmacSHA512,HmacSM3 都会出类似的问题,不是hmac的算法也可能会出类似的问题,如果捏发现长度倍增,应该考虑这种情况