java使用SHA256withRSA/PSS进行验签


前言

最近业务提出了要做签名验签的一些功能,大体背景如下:
加密算法:RSA
密钥长度:2048
签名算法:SHA256withRSA/PSS 摘要算法为SHA256 填充算法PSS
需要在c#平台生成签名和摘要拿到java平台来进行验签,java端的入参为摘要和签名


一、SHA256withRSA/PSS是什么?

SHA256withRSA/PSS是一种安全的签名算法,它使用了SHA256的摘要算法、PSS的填充算法

二、使用步骤

1.引入库

引入maven依赖:

        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15on</artifactId>
            <version>1.62</version>
        </dependency>
  由于java自带的security包下的签名算法不包括SHA256withRSA/PSS,所以我们需要引入算法提供
者BC(BouncyCastleProvider) 。

2.验签方法

由于我们的入参没有原文,只有原文经过sha256之后的摘要,而java中Signature类提供的verify方法中入参为原文,所以这里无法使用Signature直接进行验签名(是否可以我也不知道知道,之间在这里卡了很久,一直希望可以直接使用java完成,但是一直无法实现。如果大家可以,要@我~~):

    /**
     * 用摘要和签名的16进制进行验签
     * @param hash    摘要 16进制
     * @param sign    签名 16进制
     * @return boolean
     * */
    public static boolean verifyWithDigest(String hash,String sign,String publicKey) {
        long thisTime = System.currentTimeMillis();
        OutputStream outputStream=null,outputStream1=null;
        File file = new File(HASHVERIFY_PREFIX);
        if (!file.exists()) {
            file.mkdir();
        }
        File hashFile = new File(HASHVERIFY_PREFIX+"/"+thisTime+"-hash");
        File signFile = new File(HASHVERIFY_PREFIX+"/"+thisTime+"-sign");
        String publicPemPath = "";
        try {
            outputStream = new FileOutputStream(hashFile);
            outputStream.write(HexUtil.decodeHex(hash));
            outputStream1 = new FileOutputStream(signFile);
            outputStream1.write(HexUtil.decodeHex(sign));
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            X509EncodedKeySpec pkcs8KeySpec = new X509EncodedKeySpec(cn.hutool.core.codec.Base64.decode(publicKey));
            PublicKey publicK = keyFactory.generatePublic(pkcs8KeySpec);
            publicPemPath = getPublicPem(publicK);
            // TODO: 这里pem存储位置要修改
            String command = "openssl pkeyutl -verify -pubin -inkey %s -sigfile %s -in %s -pkeyopt rsa_padding_mode:pss -pkeyopt rsa_pss_saltlen:32 -pkeyopt digest:sha256";
            String endCommand = String.format(command,publicPemPath,signFile.getAbsolutePath(), hashFile.getAbsolutePath());
            LOG.info(endCommand);
            String result = RSAUtil.execShellCommand(endCommand).toString();
            if (result.toLowerCase().contains("ok") || result.toLowerCase().contains("success")){
                return true;
            }else {
                return false;
            }
        }catch (Exception e){
            LOG.error("验签执行失败,{}",e);
            return false;
        }finally {
            if (outputStream != null){
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (outputStream1 != null){
                try {
                    outputStream1.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //文件删除
            hashFile.delete();
            signFile.delete();
            new File(publicPemPath).delete();
        }
    }
    

     /**
     * 调用shell执行命令
     * @param cmd shell命令
     * @return Object 输出信息 可以为空
     */
    public static Object execShellCommand(String cmd) {
        LineNumberReader br = null;
        try {
            String[] cmdA = { "/bin/sh", "-c", cmd };
            Process process = Runtime.getRuntime().exec(cmdA);
            br = new LineNumberReader(new InputStreamReader(
                    process.getInputStream()));
            StringBuffer sb = new StringBuffer();
            String line;
            while ((line = br.readLine()) != null) {
                sb.append(line).append("\n");
            }
            return sb.toString();
        } catch (Exception e) {
            LOG.error("{}",e);
        }finally {
            if (br != null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

       private static String getPublicPem(PublicKey publicKey)
            throws IOException {
        StringWriter stringWriter = new StringWriter();
        PemWriter pemWriter = new PemWriter(stringWriter);
        PemObject pemObject = new PemObject("PUBLIC KEY",publicKey.getEncoded());
        pemWriter.writeObject(pemObject);
        pemWriter.flush();
        pemWriter.close();
        File files = new File(PEMFILE_PREFIX);
        if (!files.exists()) {
            files.mkdir();
        }
        File file = new File(PEMFILE_PREFIX+"/"+System.currentTimeMillis()+"publicKey.pem");
        OutputStream outputStream = new FileOutputStream(file);
        outputStream.write(stringWriter.toString().getBytes(StandardCharsets.UTF_8));
        return file.getAbsolutePath();
    }

代码如上


整体思路为使用Openssl命令代替Java验证签名,需要将publicKey(公钥)以pem格式保存到服务器内,然后将摘要、签名和公钥传入shell命令中,在openssl中执行命令,获取结果,判断command返回的值就可以了。


总结

由于参数没有原值,所以我在验签的地方卡了很久,最终采用了java+openssl的方式实现,虽然实现了,但是作为一名Java开发来说,这样完成并不爽,但是又暂时没有其他替代方式,如果大家有其他解决方案可以@我~

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在.NET/C#中可以使用`RSACryptoServiceProvider`类来实现SHA1WithRSA数字签名验证。 以下是一个示例代码,展示如何使用`RSACryptoServiceProvider`类在.NET/C#中实现SHA1WithRSA数字签名验证: ```csharp using System; using System.Security.Cryptography; using System.Text; class Program { static void Main(string[] args) { // 加载私钥 string privateKey = "<RSAKeyValue><Modulus>...</Modulus><Exponent>...</Exponent><P>...</P><Q>...</Q><DP>...</DP><DQ>...</DQ><InverseQ>...</InverseQ><D>...</D></RSAKeyValue>"; RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); rsa.FromXmlString(privateKey); // 加载消息 string message = "This is a test message."; // 计算SHA1哈希值 SHA1 sha1 = SHA1.Create(); byte[] hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(message)); // 使用RSA私钥对SHA1哈希值进行签名 byte[] signature = rsa.SignHash(hash, CryptoConfig.MapNameToOID("SHA1")); // 打印签名结果 Console.WriteLine("Signature: {0}", BitConverter.ToString(signature)); // 验证签名 bool result = rsa.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), signature); Console.WriteLine("Verification result: {0}", result); } } ``` 在上面的代码中,我们首先加载了RSA私钥,然后计算了消息的SHA1哈希值,最后使用RSA私钥对SHA1哈希值进行签名,并且通过`VerifyHash`方法验证签名是否有效。签名结果可以通过打印`signature`数组来查看。请注意,此示例代码仅用于演示目的,实际使用时需要注意安全性问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值