hmac php java结果不一样问题
-
比如我们有个服务是PHP提供的,要求的签名方式 hmacSha256取摘要,然后 Base64编码转化成可见字符。
-
PHP那边的源码是这样的
$result = base64_encode(hash_hmac("SHA256" , "内容" , '密钥')); echo $result;我们放到php在线调试平台取看看执行结果:
![]()
-
然后看看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 ) ); }执行结果如下:
![]()
-
明显Java的比php的结果短了一半
-
修改代码看看真实的签名长度
@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 ); }执行结果:
![]()
-
明显PHP不止256位,但是前面用的算法确实是 sha256?为啥呢?原因是PHP那边用的字符串编码方式是源文件的编码方式。Java 用的unicode码,8个和二进制位表示一个字符(一个字节),PHP字符串根据源码编码方式不同,可能占用的是16位表示一个字符。所以PHP的长了一倍,变成了512位。
-
怎么解决?其实16位(2字节)表示一个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(); } -
最终结果
@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; }运行结果结果:
![]()
长度是一样的了,核对签名内容也是一样的,sha256的算法得到256位,强行填充成了,512位,实在没必要。
如果是别人给你提供的服务那么你只能按照别的的规矩来,如果你是PHP服务的提供方,我建议源文件用unicode编码,毕竟sha256算法算出来的摘要的结果本来就应该是256位。
-
hmac 常见算法 HmacMD5,HmacSHA1,HmacSHA256,HmacSHA384,HmacSHA512,HmacSM3 都会出类似的问题,不是hmac的算法也可能会出类似的问题,如果你发现长度倍增,应该考虑这种情况
能耍的时候就一定要耍,不能耍的时候一定要学。
--天道酬勤,贵在坚持posted on 2023-01-04 22:02 zhangyukun 阅读(1166) 评论(0) 收藏 举报




浙公网安备 33010602011771号