解决不同语言使用 SHA512 配合 Base64 签名不一致的问题

序言

最近女朋友对接的 OpenAPI 是使用 SHA512 加密 + Base64 编码的签名进行鉴权,Demo 是用 JAVA 写的,PHP 得到的签名总是和 JAVA Demo 得到的不一致

问题分析

  • 首先,字符串是固定顺序拼接的,所以不存在排序问题。
  • 个人用 Go 语言实现了一下,结果与 JAVA 一致,代码如下:
package main

import (
	"crypto/sha512"
	"encoding/base64"
	"fmt"
)

func main() {
	s := `example str`
	h := sha512.New()
	h.Write([]byte(s))
	fmt.Printf("%s\n", base64.StdEncoding.EncodeToString(h.Sum(nil)))
}
  • 首先排查下是不是 PHP SHA512 的问题,先用 PHP 进行 SHA512 然后和 Go 进行 SHA512 得到的计算结果对比,结果一致

排除掉 sha512 加密方法本身的问题

  • 使用 Go 直接进行 Base64 编码 SHA512 加密的字符串,发现结果和 PHP 一致

排除掉 Base64 编码方法本身的问题

package main

import (
	"crypto/sha512"
	"encoding/base64"
	"fmt"
)

func main() {
	s := `example str`
	h := sha512.New()
	h.Write([]byte(s))
	fmt.Printf("%s\n", base64.StdEncoding.EncodeToString(h.Sum(nil)))
	// 这个字符串是 SHA512 加密后打印出来的
	fmt.Printf("%s\n", base64.StdEncoding.EncodeToString([]byte("8c4e3bb6304fa823b33dd008fb1bb5e9f400f0fad03870b5f248a9158f5cb4e4e5eca10b4f490d43590a2138886ca5a066ff27ba41c1b19dd7629175b9b01eca")))
}

问题原因

PHP 本身的 SHA512 和 Base64 都没有问题,但是由于 PHP 没有 byte 类型导致进行 SHA512 加密之后会把字符串编码成 16 进制用于显示,这时候对编码成 16 进制的字符串在进行 Base64 ,签名肯定是不一致的,所以需要进行 hex2bin 解码之后再进行签名。

解决方案实现

<?php
$str="example str";
$sha512=hash("sha512",$str);
echo base64_encode(hex2bin($sha512));