解决 PHP & JAVA 使用 SHA512 配合 Base64 签名不一致的问题

描述

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

问题分析

  • 首先,字符串是固定顺序拼接的,所以不存在排序问题。
  • 个人用 Go 语言实现了一下,结果与 JAVA 一致
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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 编码方法本身的问题

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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 解码之后再进行签名。

解决方案实现

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

解决 PHP & JAVA 使用 SHA512 配合 Base64 签名不一致的问题
https://agopher.com/2020/09/24/tech/2020-sign/
作者
冷宇生(Allen)
发布于
2020年9月24日
许可协议