一、异常情况描述
笔者在做 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 算法也在支持的签名算法列表中。
再次运行签名方法,异常问题已经被修复,能正确对数据进行签名。