java.security.NoSuchAlgorithmException: SHA256WithRSAandMGF1 Signature not available

9 篇文章 1 订阅
一、异常情况描述

  笔者在做 RSA 签名时,遇到了一个异常,详见下文。

14:04:33.622 [main] ERROR com.test.util.SignUtil - NoSuchAlgorithmException
java.security.NoSuchAlgorithmException: SHA256WithRSAandMGF1 Signature not available
	at java.security.Signature.getInstance(Signature.java:229)
	at com.test.util.SignUtil.signData(SignUtil.java:29)
	at com.test.util.SignUtil.main(SignUtil.java:49)
二、签名方法代码

  以下是做签名的 Java 代码。

package com.test.util;

import lombok.extern.slf4j.Slf4j;

import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;

@Slf4j
public class SignUtil {

    /**
     * RSA signature 私钥签名
     *
     * @param data          data to be signed
     * @param privateKeystr privatekey
     * @param signMode      签名模式
     * @return signature result
     */
    public static String signData(String data, String privateKeystr, String signMode) {
        try {
            Base64.Decoder decoder = Base64.getDecoder();
            byte[] keyBytes = decoder.decode(privateKeystr);
            PKCS8EncodedKeySpec keyspec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey privateKey = keyFactory.generatePrivate(keyspec);
            Signature signature = Signature.getInstance(signMode);
            signature.initSign(privateKey);
            signature.update(data.getBytes(StandardCharsets.UTF_8));
            byte[] mi = signature.sign();
            Base64.Encoder encoder = Base64.getEncoder();
            return encoder.encodeToString(mi);
        } catch (NoSuchAlgorithmException e) {
            log.error("NoSuchAlgorithmException", e);
        } catch (InvalidKeyException e) {
            log.error("InvalidKeyException", e);
        } catch (InvalidKeySpecException e) {
            log.error("InvalidKeySpecException", e);
        } catch (SignatureException e) {
            log.error("SignatureException", e);
        }
        return null;
    }

    public static void main(String[] args) {
        String data = "sssssaaaaaaa";
        String signCnt = signData(data,
                "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",
                "SHA256WithRSAandMGF1");
        System.out.println(signCnt);
    }
}

三、异常原因分析

  SHA256WithRSAandMGF1 是 BC Provider 提供的一个签名算法,而 JDK 1.8 的 java.security.* 包中并没有这个 Provider。运行如下代码,可以打印 JDK 中的 Provider 列表,以及所有签名算法。

    public static void main(String[] args) {
        TreeSet<String> algorithms = new TreeSet<>();

        Provider[] providers = Security.getProviders();
        System.out.println("-----Provider 列表如下:-----");
        for (Provider provider : providers) {
            System.out.println(provider.getName());
        }

        System.out.println("-----支持的签名算法如下:-----");

        for (Provider provider : providers) {
            for (Provider.Service service : provider.getServices())
                if (service.getType().equals("Signature")) {
                    algorithms.add(service.getAlgorithm());
                }
        }

        for (String algorithm : algorithms) {
            System.out.println(algorithm);
        }
    }

  运行结果如下所示:

-----Provider 列表如下:-----
SUN
SunRsaSign
SunEC
SunJSSE
SunJCE
SunJGSS
SunSASL
XMLDSig
SunPCSC
Apple
-----支持的签名算法如下:-----
MD2withRSA
MD5andSHA1withRSA
MD5withRSA
NONEwithDSA
NONEwithECDSA
SHA1withDSA
SHA1withECDSA
SHA1withRSA
SHA224withDSA
SHA224withECDSA
SHA224withRSA
SHA256withDSA
SHA256withECDSA
SHA256withRSA
SHA384withECDSA
SHA384withRSA
SHA512withECDSA
SHA512withRSA

  由结果可见,SHA256WithRSAandMGF1 签名算法确实不支持。

四、解决方法
  Step 1:在项目的 pom.xml 文件中添加如下依赖包,或者以 jar 文件的方式添加到项目中。
		<dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15on</artifactId>
            <version>1.60</version>
        </dependency>
  Step 2:在签名工具类中添加如下静态代码块,将 BouncyCastleProvider 添加到 Provider 列表中。
	static {
        Security.addProvider(new BouncyCastleProvider());
    }

  再次运行第三章中的代码,最新输出如下所示。

-----Provider 列表如下:-----
SUN
SunRsaSign
SunEC
SunJSSE
SunJCE
SunJGSS
SunSASL
XMLDSig
SunPCSC
Apple
BC
-----支持的签名算法如下:-----
1.2.840.113549.1.1.10
DDSA
DETDSA
DSA
DSTU4145
ECDDSA
ECDSA
ECGOST3410
ECGOST3410-2012-256
ECGOST3410-2012-512
GOST3410
GOST3411-2012-256WITHECGOST3410-2012-256
GOST3411-2012-512WITHECGOST3410-2012-512
GOST3411WITHDSTU4145
GOST3411WITHDSTU4145LE
GOST3411WITHECGOST3410
MD2WITHRSA
MD2withRSA
MD4WITHRSA
MD5WITHRSA
MD5WITHRSA/ISO9796-2
MD5andSHA1withRSA
MD5withRSA
NONEWITHDSA
NONEwithDSA
NONEwithECDSA
OID.1.2.840.113549.1.1.10
RAWRSASSA-PSS
RIPEMD128WITHRSA
RIPEMD128WITHRSA/X9.31
RIPEMD160WITHECDSA
RIPEMD160WITHPLAIN-ECDSA
RIPEMD160WITHRSA
RIPEMD160WITHRSA/X9.31
RIPEMD160withRSA/ISO9796-2
RIPEMD256WITHRSA
RMD128WITHRSA
RMD128WITHRSA/X9.31
RMD160WITHRSA
RMD160WITHRSA/X9.31
RMD256WITHRSA
RSA
RSASSA-PSS
SHA1WITHCVC-ECDSA
SHA1WITHDDSA
SHA1WITHDETDSA
SHA1WITHECDDSA
SHA1WITHECNR
SHA1WITHPLAIN-ECDSA
SHA1WITHRSA
SHA1WITHRSA/ISO9796-2
SHA1WITHRSA/X9.31
SHA1WITHRSAANDMGF1
SHA1withDSA
SHA1withECDSA
SHA1withRSA
SHA224WITHCVC-ECDSA
SHA224WITHDDSA
SHA224WITHDETDSA
SHA224WITHDSA
SHA224WITHECDDSA
SHA224WITHECDSA
SHA224WITHECNR
SHA224WITHPLAIN-ECDSA
SHA224WITHRSA
SHA224WITHRSA/ISO9796-2
SHA224WITHRSA/X9.31
SHA224WITHRSAANDMGF1
SHA224withDSA
SHA224withECDSA
SHA224withRSA
SHA256WITHCVC-ECDSA
SHA256WITHDDSA
SHA256WITHDETDSA
SHA256WITHDSA
SHA256WITHECDDSA
SHA256WITHECDSA
SHA256WITHECNR
SHA256WITHPLAIN-ECDSA
SHA256WITHRSA
SHA256WITHRSA/ISO9796-2
SHA256WITHRSA/X9.31
SHA256WITHRSAANDMGF1
SHA256withDSA
SHA256withECDSA
SHA256withRSA
SHA3-224WITHDDSA
SHA3-224WITHDSA
SHA3-224WITHECDDSA
SHA3-224WITHECDSA
SHA3-224WITHRSA
SHA3-224WITHRSAANDMGF1
SHA3-256WITHDDSA
SHA3-256WITHDSA
SHA3-256WITHECDDSA
SHA3-256WITHECDSA
SHA3-256WITHRSA
SHA3-256WITHRSAANDMGF1
SHA3-384WITHDDSA
SHA3-384WITHDSA
SHA3-384WITHECDDSA
SHA3-384WITHECDSA
SHA3-384WITHRSA
SHA3-384WITHRSAANDMGF1
SHA3-512WITHDDSA
SHA3-512WITHDSA
SHA3-512WITHECDDSA
SHA3-512WITHECDSA
SHA3-512WITHRSA
SHA3-512WITHRSAANDMGF1
SHA384WITHCVC-ECDSA
SHA384WITHDDSA
SHA384WITHDETDSA
SHA384WITHDSA
SHA384WITHECDDSA
SHA384WITHECDSA
SHA384WITHECNR
SHA384WITHPLAIN-ECDSA
SHA384WITHRSA
SHA384WITHRSA/ISO9796-2
SHA384WITHRSA/X9.31
SHA384WITHRSAANDMGF1
SHA384withECDSA
SHA384withRSA
SHA512(224)WITHRSA
SHA512(224)WITHRSA/ISO9796-2
SHA512(224)WITHRSA/X9.31
SHA512(224)WITHRSAANDMGF1
SHA512(256)WITHRSA
SHA512(256)WITHRSA/ISO9796-2
SHA512(256)WITHRSA/X9.31
SHA512(256)WITHRSAANDMGF1
SHA512WITHCVC-ECDSA
SHA512WITHDDSA
SHA512WITHDETDSA
SHA512WITHDSA
SHA512WITHECDDSA
SHA512WITHECDSA
SHA512WITHECNR
SHA512WITHPLAIN-ECDSA
SHA512WITHRSA
SHA512WITHRSA/ISO9796-2
SHA512WITHRSA/X9.31
SHA512WITHRSAANDMGF1
SHA512withECDSA
SHA512withRSA
SM3WITHSM2
WhirlpoolWITHRSA/ISO9796-2
WhirlpoolWITHRSA/X9.31

  可以看到,BC Provider 已经被添加到列表中,且 SHA256WithRSAandMGF1 算法也在支持的签名算法列表中。

  再次运行签名方法,异常问题已经被修复,能正确对数据进行签名。

文章参考:
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智能识别收货地址Java import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.security.InvalidKeyException; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.TimeZone; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import sun.misc.BASE64Encoder; class Demo { public static String calcAuthorization(String source, String secretId, String secretKey, String datetime) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException { String signStr = "x-date: " + datetime + "\n" + "x-source: " + source; Mac mac = Mac.getInstance("HmacSHA1"); Key sKey = new SecretKeySpec(secretKey.getBytes("UTF-8"), mac.getAlgorithm()); mac.init(sKey); byte[] hash = mac.doFinal(signStr.getBytes("UTF-8")); String sig = new BASE64Encoder().encode(hash); String auth = "hmac id=\"" + secretId + "\", algorithm=\"hmac-sha1\", headers=\"x-date x-source\", signature=\"" + sig + "\""; return auth; } public static String urlencode(Map<?, ?> map) throws UnsupportedEncodingException { StringBuilder sb = new StringBuilder(); for (Map.Entry<?, ?> entry : map.entrySet()) { if (sb.length() > 0) { sb.append("&"); } sb.append(String.format("%s=%s", URLEncoder.encode(entry.getKey().toString(), "UTF-8"), URLEncoder.encode(entry.getValue().toString(), "UTF-8") )); } return sb.toString(); } public static void main(String[] args) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException { //云市场分配的密钥Id String secretId = "xxxx"; //云市场分配的密钥Key String secretKey = "xxxx"; String source = "market"; Calendar cd = Calendar.getInstance(); SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Lo

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值