php实现rsa签名和验签

这两天在弄某支付接口相关的东西,以前没做过这块,在签名和验签的过程中遇到了一些问题,记下来.

首先生成一个1024位的私钥:

openssl genrsa -out private.pem 1024

然后根据私钥导出公钥

openssl rsa -in private.pem -pubout -out public.pem

php的openssl扩展里已经封装好了签名和验签的方法,分别是openssl_sign和openssl_verify.

function sign($data){
    $p = openssl_pkey_get_private(file_get_contents('private.pem'));
    openssl_sign($data, $signature, $p);
    openssl_free_key($p);
    return bin2hex($signature);
}

function verify($data, $sign){
    $p = openssl_pkey_get_public(file_get_contents('public.pem'));
    $verify = openssl_verify($data, hex2bin($sign), $p);
    openssl_free_key($p);
    return $verify > 0;
}

实际情况是测试接口并没有提供公私钥,而是提供了公/私钥指数,模数.通过java的RSAPrivateKeySpec和RSAPublicKeySpec来实现签名和验签,遂写了一个jar用命令行来调用:

package org;
     
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
     
/**
 * @author eslizn
 *
 */
public class SignVerify {
     
     /**
     * @param args
     * @throws Exception
     */
     public static void main(String[] args) throws Exception {
         if(args.length == 4 && args[0].equals("sign")){
             System.out.println(Sign(args[1], new BigInteger(args[2], 16), new BigInteger(args[3], 16)));
             System.exit(0);
         }
     
         if(args.length == 5 && args[0].equals("verify")){
             System.out.println(Verify(args[1], args[2], new BigInteger(args[3], 16), new BigInteger(args[4], 16)) ? "1" : "0");
             System.exit(0);
         }
     }
     
     /**
     * Sign
     *
     * @param data
     * @param mod
     * @param exp
     * @return
     * @throws Exception
     */
     public static String Sign(String data, BigInteger mod, BigInteger exp) throws Exception{
         RSAPrivateKeySpec spec = new RSAPrivateKeySpec(mod, exp);
         KeyFactory keyFactory = KeyFactory.getInstance("RSA");
         PrivateKey signKey = keyFactory.generatePrivate(spec);
         Signature signature = Signature.getInstance("SHA1withRSA");
         signature.initSign(signKey);
         signature.update(data.getBytes());
         return byteArray2HexString(signature.sign());
     }
     
     /**
     * Verify
     *
     * @param data
     * @param sign
     * @param mod
     * @param exp
     * @return
     * @throws Exception
     */
     public static boolean Verify(String data, String sign, BigInteger mod, BigInteger exp) throws Exception{
         RSAPublicKeySpec spec = new RSAPublicKeySpec(mod, exp);
         KeyFactory keyFactory = KeyFactory.getInstance("RSA");
         java.security.PublicKey verifyKey = keyFactory.generatePublic(spec);
         Signature verifier = Signature.getInstance("SHA1withRSA");
         verifier.initVerify(verifyKey);
         verifier.update(data.getBytes());
         return verifier.verify(hexString2ByteArray(sign));
     }
     
     public static byte[] hexString2ByteArray(String hexStr){
         if (hexStr == null)
             return null;
         if (hexStr.length() % 2 != 0)
             return null;
         byte data[] = new byte[hexStr.length() / 2];
         for (int i = 0; i < hexStr.length() / 2; i++){
             char hc = hexStr.charAt(2 * i);
             char lc = hexStr.charAt(2 * i + 1);
             byte hb = hexChar2Byte(hc);
             byte lb = hexChar2Byte(lc);
             if (hb < 0 || lb < 0)
                 return null;
             int n = hb << 4;
             data[i] = (byte)(n + lb);
         }
        return data;
    }
     
    public static byte hexChar2Byte(char c){
        if (c >= '0' && c <= '9')
            return (byte)(c - 48);
        if (c >= 'a' && c <= 'f')
            return (byte)((c - 97) + 10);
        if (c >= 'A' && c <= 'F')
            return (byte)((c - 65) + 10);
        else
            return -1;
    }
     
    public static String byteArray2HexString(byte arr[]){
        StringBuilder sbd = new StringBuilder();
        byte arr$[] = arr;
        int len$ = arr$.length;
        for (int i$ = 0; i$ < len$; i$++){
        byte b = arr$[i$];
        String tmp = Integer.toHexString(0xff & b);
        if (tmp.length() < 2)
            tmp = (new StringBuilder()).append("0").append(tmp).toString();
            sbd.append(tmp);
        }
        return sbd.toString();
    }
}

巧合的是写完后通过沟通,对方要我们自行生成一对公私钥后将其中的公/私钥指数和模数发给他们.有的时候可能有的办法比技术实现更方便.附上取公/私钥指数和模数的代码:

function getPrivate($file){
    $p = openssl_pkey_get_private(file_get_contents($file));
    $res = openssl_pkey_get_details($p);
    var_dump($res);
    openssl_free_key($p);
    return array(
        'n' => bin2hex($res['rsa']['n']),#模数
        'e' => bin2hex($res['rsa']['e']),#公钥指数
        'd' => bin2hex($res['rsa']['d']),#私钥指数
    );
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏小工匠技术圈

【小工匠聊密码学】--对称加密--3DES

  3DES(或称为Triple DES)是三重数据加密算法(TDEA,Triple Data Encryption Algorithm)块密码的通称。它相当于...

822
来自专栏函数式编程语言及工具

Scalaz(38)- Free :Coproduct-Monadic语句组合

   很多函数式编程爱好者都把FP称为Monadic Programming,意思是用Monad进行编程。我想FP作为一种比较成熟的编程模式,应该有一套比较规范...

17810
来自专栏小工匠技术圈

【小工匠聊密码学】--消息摘要--SHA算法

1395
来自专栏Ldpe2G的个人博客

乱序+移位加密24位bmp格式图片 scala实现

1642
来自专栏ccylovehs

武电实业卡密接口对接经验分享

以往卡密都是电信直接提供txt文件我们这边再导入数据库中,现在电信要求厂家获取卡密必须通过接口调用,此次对接主要用到三个方面知识:

641
来自专栏编程之旅

AES加密——Java与iOS的解决方案

该算法为比利时密码学家Joan Daemen和Vincent Rijmen所设计,结合两位作者的名字,以Rijndael为名投稿高级加密标准的甄选流程。(Rij...

1765
来自专栏Python小屋

Python使用AES算法进行加解密

本文要点在于Python扩展库pycrypto实现了大量密码学算法,可以拿来直接使用。 import string import random from Cry...

2666
来自专栏码匠的流水账

java加解密实例

532
来自专栏Hongten

PBE_Password-based encryption(基于密码加密)_项目中你也可以有

中说道了PBE——Password-based encryption(基于密码加密)。我也做测试了一下,现在把我做的效果给大家演示一下:

691
来自专栏Android干货

浅谈DES加密算法

3095

扫码关注云+社区