资讯专栏INFORMATION COLUMN

【渣渣程序员踩过的坑】PHP的hash_hmac签名加密,PHP迷一样的base64_encode

henry14 / 3832人阅读

摘要:如何获取受支持的算法清单,请参见。要进行哈希运算的消息。使用生成信息摘要时所使用的密钥。设置为输出原始二进制数据,设置为输出小写进制字符串。

介绍一下问题的背景:

本人一枚小小PHPer,有一天公司的Java找到我,让我帮忙写一个接口的Demo,心想:‘最喜欢写接口了,来来来来!’,于是Java就带着Java版Demo来了,大概看了一遍,具体涉及以下几点:(不想了解的看最后一部分,就好了,那是中心思想)

md5加密:

java中定义hashMap,储存userid,再使用toJSONString将其转换成Json串,再将Json使用md5Hex加密,再放入hashMap中

map参数格式转换:

将map中的数据转换成String,对key,value进行数据拼接,拼接成字符串,此字符串具体要求如下:
‘按照参数名ASCII码从小到大排序,使用URL键值对的格式(即key1=value1&key2=value2…)构造成字符串signPlainText’

hash_hmac签名验证:

这个没什么说的,PHP中有hash_hmac函数

谈论一下遇到的坑:

本PHPer比较渣渣,不知道Java中的hashMap是用来做啥的,但我知道,它最后做了一件事:JSONObject.toJSONString(body),没错,转换成字符串了,所以有了以下代码:(不负责的贴图,不知道正确与否)

md5加密

Java版

            Map body = new HashMap<>();
            body.put("userId", userId);
            //post请求body为json格式,将json格式进行md5加密
            String postBody = JSONObject.toJSONString(body);
            String bodyMd = DigestUtils.md5Hex(postBody);

PHP版

            $body = ["userId"=>$userId];
            $postbody = json_encode($body);
            $bodyMd = md5($postbody);

上边我们之间应该是在做一件事请吧?应该是的,反正都是转成了Json,转成了Md5

接下来就是map转换string了,再贴两段代码:(不负责的贴,不知道正确与否)
(按照参数名ASCII码从小到大排序,使用URL键值对的格式(即key1=value1&key2=value2…)构造成字符串signPlainText)

map参数格式转换:

Java版

        Map resultMap = new TreeMap<>();
        for (Map.Entry entry : map.entrySet()) {
            String key = (String) entry.getKey();
            Object value = entry.getValue();
            if (StringUtils.isEmpty(key) || value == null) {
                continue;
            }
            resultMap.put(key, value);
        }
        StringBuilder buff = new StringBuilder();
        for (Map.Entry entry : resultMap.entrySet()) {
            buff.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
        }
        String signPlanText = buff.toString();
        if (StringUtils.isNotEmpty(signPlanText)) {
            signPlanText = signPlanText.substring(0, signPlanText.length() - 1);
        }
        return signPlanText;

PHP版

        $signPlanText = "";
        ksort($params);
        foreach ($params as $key => $param){
            if(empty($key) || $param == null){
                continue;
            }
            $signPlanText .= $key."=".$param."&";
        }
        if($signPlanText){
            $signPlanText = substr($signPlanText,0,strlen($signPlanText)-1);
        }
        return $signPlanText;

遇到的问题: 因为Java中Treemap是有序的,按照自然值进行排序,但PHP中好像没有这样一个东西,所以决定使用Ksort来进行排序,不知道是否正确!应该是正确的吧?各位大神拍砖~

HmacSHA256签名验证:

Java版

        String signature = "";
        try {
            Mac sha256HMAC = Mac.getInstance(HMACSHA256);
            SecretKeySpec secretkey = new SecretKeySpec(appSecret.getBytes(StandardCharsets.UTF_8), HMACSHA256);
            sha256HMAC.init(secretkey);
            byte[] bytes = sha256HMAC.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
            signature = Base64.encodeBase64URLSafeString(bytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return signature;

PHP版( 呀?看起来是不是没有问题?)

        $hmac_sha256_str = base64_encode(hash_hmac("sha256", $signPlanText, $appSecret));
        $signature = urlencode($hmac_sha256_str);
        return $signature;

还在想,PHP这么点代码就解决了?不愧是世界上最好的语言’,(大佬们,我加引号了)
23:35分终于翻译完以上Java代码,放一首歌,美美的睡觉,心想明天可以美美的交工~ (滴滴滴滴滴滴等待~地理地理等待~~)

坑来了,运行上边代码,你会发现,接口提示签名错误!!错误一直是错误!!为什么呢???明明是一样的啊!百思不得其解!!

继续往下看···

再看PHP文档中的hash_hmac的声明:

string hash_hmac ( string $algo , string $data , string $key [, bool $raw_output = FALSE ] )

algo
要使用的哈希算法名称,例如:"md5","sha256","haval160,4" 等。 如何获取受支持的算法清单,请参见 hash_algos()。

data
要进行哈希运算的消息。

key
使用 HMAC 生成信息摘要时所使用的密钥。

raw_output 设置为 TRUE 输出原始二进制数据, 设置为 FALSE 输出小写 16 进制字符串。

在Java中sha256HMAC后得到的值为二进制,So,PHP也要转换为二进制,所以改进为以下代码:

        hash_hmac("sha256", $signPlanText, $appSecret,true);//由此生成出的为二进制格式

这还没完,最重要的出现了:

java中Base64.encodeBase64URLSafeString(bytes) 会将 特殊字符‘+/’替换为"-_",会将‘=’去掉

但是!!PHP不会!!! PHP 没有提供url安全的base64编码函数,需要自己手动去撸!还是自己水平不够

So,出现了以下代码,也是最终的解决方案:

     /**
     * 签名验证
     */
    public static function generateSHASign($signPlanText,$appSecret){
        $signature = self::base64UrlEncode(hash_hmac("sha256",$signPlanText, $appSecret, true));
        return $signature;
    }

    /**
     * 替换特殊符号
     */
    public static function base64UrlEncode($str){
       return rtrim(strtr(base64_encode($str),"+/","-_"),"=");
    }

这时,你会发现,签名正确,原来生活如此美好、怪我太年轻


同时有朋友说:
1.是有两套不相同的标准,Ios一套,Java一套
2.在PHP中url_encode这个函数,分两种: urlencode 和 rawurlencode

两者的差异:urlencode和rawurlencode两个方法在处理字母数字,特殊符号,中文的时候结果都是一样的,唯一的不同是对空格的处理,urlencode处理成“+”,rawurlencode处理成%20

总结下来就是:

php的base64_encode和base64_decode用来转换url是不符合要求,所以需要自己实现方法

base64url_decode

    public static function base64url_decode($data)
    {
        return base64_decode(str_pad(strtr($data, "-_", "+/"), strlen($data) % 4, "=", STR_PAD_RIGHT));
    }

base64url_encode

    public static function base64url_encode($data)
    {
        return rtrim(strtr(base64_encode($data), "+/", "-_"), "=");
    }

终是晓梦迷了蝶,你是恩赐也是劫!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/30011.html

相关文章

  • 渣渣序员踩过PHPhash_hmac签名加密PHP一样base64_encode

    摘要:如何获取受支持的算法清单,请参见。要进行哈希运算的消息。使用生成信息摘要时所使用的密钥。设置为输出原始二进制数据,设置为输出小写进制字符串。 介绍一下问题的背景: 本人一枚小小PHPer,有一天公司的Java找到我,让我帮忙写一个接口的Demo,心想:‘最喜欢写接口了,来来来来!’,于是Java就带着Java版Demo来了,大概看了一遍,具体涉及以下几点:(不想了解的看最后一部分,就好...

    Rocture 评论0 收藏0
  • 带你弄懂JWT原理

    摘要:的组成一个实际上就是一个字符串,它由三部分组成,头部载荷与签名。这个字符串我们将它称作的载荷。注意是一种编码,它是可以被翻译回原来的样子来的。这也可以被表示成一个对象。 JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。 让我们来假想一下一个场景。在A用户关注了B用户的时候,系统发邮件给B用户,并且附有一个链接点...

    animabear 评论0 收藏0
  • yii2加密解密那些事儿

    摘要:我们做程序的时候,加密解密是绕不开的话题,使用开发应用的时候,都内置了哪些有关加密解密安全方便的支持那本文将为你揭晓。函数声明为存在着第三个参数,比如我们可以传递会员的等,这样此信息将和一起作为加密解密的钥匙。 我们做程序的时候,加密解密是绕不开的话题,使用yii2开发应用的时候,都内置了哪些有关加密解密(安全)方便的支持那?本文将为你揭晓。 相关环境 操作系统及IDE macOS ...

    dendoink 评论0 收藏0
  • PHP hash 接口对接

    摘要:另外说到请求对方接口的问题,对方要求传递一个参数过去,比如结果提示但是没问题于是对参数再进行一次但对方有时候接受的参数解析出错于是有了如下处理传递时也改成安全 最近接一个项目,需要调用对方接口生成 token 但只提供了 node 版,源代码如下 //https://blog.zhengxianjun.com/2015/05/javascript-crypto-js/ npm inst...

    kel 评论0 收藏0
  • PHP里使用ImageMagick生成base64图片

    摘要:个人博客本文原地址最近的项目中,需要用到画图和图片拼接效果,这里是一些开发过程里用到的一些点还有就是一些踩过的坑。通过生成图片格式,为前端所使用。需要注意的是前端得到的额数据里包含有回车字符,需要特殊处理才可以正确显示图片。 个人博客 https://duanruilong.github.io/blog/本文原地址https://duanruilong.github.io/blog/2...

    supernavy 评论0 收藏0

发表评论

0条评论

henry14

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<