java中的签名和证书那些事

java中的签名和证书那些事1.数字签名数字签名,简单来说就是通过提供 可鉴别 的 数字信息 验证 自身身份 的一种方式。一套 数字签名 通常定义两种互补的运算,一个用于 签名,另一个用于 验证。分别由 发送者 持有能够 代表自己身份 的 私钥 (私钥不可泄露),由 接受者 持有与私钥对应的 公钥 ,能够在 接受 到来自发送者信息时用于 验证 其身份。签名 最根本的用途是要能够唯一 证明发送方的身份,防止 中间人攻击、CSRF跨域身份伪造。基于这一点在诸如 设备认证、用户认证、第三方认证 等认证体系中都会使用到签名算法。

2. 加密数字签名是基于加密算法来实现的。加密算法可以用来保护明文不被非法窃取和使用。加密算法主要分为对称加密和非对称加密两种。

2.1 对称加密对称加密算法的加密与解密密钥相同,还有一些不需要密钥的散列算法。

常见的对称加密算法主要有 DES、3DES、AES 等,散列算法主要有 SHA-1、MD5 等。

2.2 非对称加密它需要两个密钥,一个称为 公开密钥 (public key),即 公钥,另一个称为 私有密钥 (private key),即 私钥。

使用公钥对数据进行加密,只有私钥才能进行解密。使用私钥对数据进行加密,只有公钥才能进行解密。常见的 非对称算法 主要有 RSA、DSA 等

rsa加密有两种使用方式:

第一是对文件内容加密,这种用途需要发送方用公钥对文件加密,接收方用私钥对文件解密。这种方式下,文件在网络传输中都是密文,那么在发送方要用rsa公钥加密.接收方用私钥解密.所以只有私钥的接收方才能解密,看到原文.这是rsa单纯用于文件加密的用途.

第二是对文件的sha256签名进行加密,这种方式下,发送方要用私钥对签名进行加密,接收方用公钥进行解密。这种方式下,原文件不加密,rsa与sha265签名算法, 生成的密文放在文件的开头。可完成对文件的验证.即该文件在传输过程中有没有被修改过.如果被修改过,即验证失败.而crc校验,只能验证文件的完整性. 如果被修改, 则验证不出来.

公钥与私钥标准:

PKCS8是私钥证书标准.X509是公钥证书标准.3. 支付宝支付中的公钥与私钥3.1 私钥的处理参见AlipaySignature类中的代码:

代码语言:javascript代码运行次数:0运行复制 /** * rsa内容签名 * * @param content * @param privateKey * @param charset * @return * @throws AlipayApiException */ public static String rsaSign(String content, String privateKey, String charset, String signType) throws AlipayApiException {

if (AlipayConstants.SIGN_TYPE_RSA.equals(signType)) {

return rsaSign(content, privateKey, charset); } else if (AlipayConstants.SIGN_TYPE_RSA2.equals(signType)) {

return rsa256Sign(content, privateKey, charset); } else {

throw new AlipayApiException("Sign Type is Not Support : signType=" + signType); }

}

/** * sha256WithRsa 加签 * * @param content * @param privateKey * @param charset * @return * @throws AlipayApiException */ public static String rsa256Sign(String content, String privateKey, String charset) throws AlipayApiException {

try { PrivateKey priKey = getPrivateKeyFromPKCS8(AlipayConstants.SIGN_TYPE_RSA, new ByteArrayInputStream(privateKey.getBytes()));

java.security.Signature signature = java.security.Signature .getInstance(AlipayConstants.SIGN_SHA256RSA_ALGORITHMS);

signature.initSign(priKey);

if (StringUtils.isEmpty(charset)) { signature.update(content.getBytes()); } else { signature.update(content.getBytes(charset)); }

byte[] signed = signature.sign();

return new String(Base64.encodeBase64(signed)); } catch (Exception e) { throw new AlipayApiException("RSAcontent = " + content + "; charset = " + charset, e); }

} /** * sha1WithRsa 加签 * * @param content * @param privateKey * @param charset * @return * @throws AlipayApiException */ public static String rsaSign(String content, String privateKey, String charset) throws AlipayApiException { try { PrivateKey priKey = getPrivateKeyFromPKCS8(AlipayConstants.SIGN_TYPE_RSA, new ByteArrayInputStream(privateKey.getBytes()));

java.security.Signature signature = java.security.Signature .getInstance(AlipayConstants.SIGN_ALGORITHMS);

signature.initSign(priKey);

if (StringUtils.isEmpty(charset)) { signature.update(content.getBytes()); } else { signature.update(content.getBytes(charset)); }

byte[] signed = signature.sign();

return new String(Base64.encodeBase64(signed)); } catch (InvalidKeySpecException ie) { throw new AlipayApiException("RSA私钥格式不正确,请检查是否正确配置了PKCS8格式的私钥", ie); } catch (Exception e) { throw new AlipayApiException("RSAcontent = " + content + "; charset = " + charset, e); } }其中rsa和rsa256的私钥使用的是getPrivateKeyFromPKCS8;

3.2 公钥的处理:参见AlipaySignature类中的代码:

代码语言:javascript代码运行次数:0运行复制 public static boolean rsaCheckV1(Map params, String publicKey, String charset,String signType) throws AlipayApiException { String sign = params.get("sign"); String content = getSignCheckContentV1(params); return rsaCheck(content, sign, publicKey, charset,signType); }

public static boolean rsaCheckV2(Map params, String publicKey, String charset) throws AlipayApiException { String sign = params.get("sign"); String content = getSignCheckContentV2(params);

return rsaCheckContent(content, sign, publicKey, charset); }

public static boolean rsaCheckV2(Map params, String publicKey, String charset,String signType) throws AlipayApiException { String sign = params.get("sign"); String content = getSignCheckContentV2(params);

return rsaCheck(content, sign, publicKey, charset,signType); }

public static boolean rsaCheck(String content, String sign, String publicKey, String charset, String signType) throws AlipayApiException {

if (AlipayConstants.SIGN_TYPE_RSA.equals(signType)) {

return rsaCheckContent(content, sign, publicKey, charset);

} else if (AlipayConstants.SIGN_TYPE_RSA2.equals(signType)) {

return rsa256CheckContent(content, sign, publicKey, charset);

} else {

throw new AlipayApiException("Sign Type is Not Support : signType=" + signType); }

}

public static boolean rsa256CheckContent(String content, String sign, String publicKey, String charset) throws AlipayApiException { try { PublicKey pubKey = getPublicKeyFromX509("RSA", new ByteArrayInputStream(publicKey.getBytes()));

java.security.Signature signature = java.security.Signature .getInstance(AlipayConstants.SIGN_SHA256RSA_ALGORITHMS);

signature.initVerify(pubKey);

if (StringUtils.isEmpty(charset)) { signature.update(content.getBytes()); } else { signature.update(content.getBytes(charset)); } return signature.verify(Base64.decodeBase64(sign.getBytes())); } catch (Exception e) { throw new AlipayApiException( "RSAcontent = " + content + ",sign=" + sign + ",charset = " + charset, e); } }

public static boolean rsaCheckContent(String content, String sign, String publicKey, String charset) throws AlipayApiException { try { PublicKey pubKey = getPublicKeyFromX509("RSA", new ByteArrayInputStream(publicKey.getBytes()));

java.security.Signature signature = java.security.Signature .getInstance(AlipayConstants.SIGN_ALGORITHMS);

signature.initVerify(pubKey);

if (StringUtils.isEmpty(charset)) { signature.update(content.getBytes()); } else { signature.update(content.getBytes(charset)); }

return signature.verify(Base64.decodeBase64(sign.getBytes())); } catch (Exception e) { throw new AlipayApiException( "RSAcontent = " + content + ",sign=" + sign + ",charset = " + charset, e); } }

public static PublicKey getPublicKeyFromX509(String algorithm, InputStream ins) throws Exception { KeyFactory keyFactory = KeyFactory.getInstance(algorithm);

StringWriter writer = new StringWriter(); StreamUtil.io(new InputStreamReader(ins), writer);

byte[] encodedKey = writer.toString().getBytes();

encodedKey = Base64.decodeBase64(encodedKey);

return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey)); }可见公钥的处理是通过getPublicKeyFromX509来处理的;

4. https的加密处理参见微信支付的代码:

方式1:对参数与key及随机串进行排序后md5;方式2: https证书签名 WXPayRequest中的代码:代码语言:javascript代码运行次数:0运行复制 /** * 请求,只请求一次,不做重试 * @param domain * @param urlSuffix * @param uuid * @param data * @param connectTimeoutMs * @param readTimeoutMs * @param useCert 是否使用证书,针对退款、撤销等操作 * @return * @throws Exception */ private String requestOnce(final String domain, String urlSuffix, String uuid, String data, int connectTimeoutMs, int readTimeoutMs, boolean useCert) throws Exception { BasicHttpClientConnectionManager connManager; if (useCert) { // 证书 char[] password = config.getMchID().toCharArray(); InputStream certStream = config.getCertStream(); KeyStore ks = KeyStore.getInstance("PKCS12"); ks.load(certStream, password);

// 实例化密钥库 & 初始化密钥工厂 KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(ks, password);

// 创建 SSLContext SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(kmf.getKeyManagers(), null, new SecureRandom());

SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory( sslContext, new String[]{"TLSv1"}, null, new DefaultHostnameVerifier());

connManager = new BasicHttpClientConnectionManager( RegistryBuilder.create() .register("http", PlainConnectionSocketFactory.getSocketFactory()) .register("https", sslConnectionSocketFactory) .build(), null, null, null ); } else { connManager = new BasicHttpClientConnectionManager( RegistryBuilder.create() .register("http", PlainConnectionSocketFactory.getSocketFactory()) .register("https", SSLConnectionSocketFactory.getSocketFactory()) .build(), null, null, null ); }

HttpClient httpClient = HttpClientBuilder.create() .setConnectionManager(connManager) .build();

String url = "https://" + domain + urlSuffix; HttpPost httpPost = new HttpPost(url);

RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(readTimeoutMs).setConnectTimeout(connectTimeoutMs).build(); httpPost.setConfig(requestConfig);

StringEntity postEntity = new StringEntity(data, "UTF-8"); httpPost.addHeader("Content-Type", "text/xml"); httpPost.addHeader("User-Agent", USER_AGENT + " " + config.getMchID()); httpPost.setEntity(postEntity);

HttpResponse httpResponse = httpClient.execute(httpPost); HttpEntity httpEntity = httpResponse.getEntity(); return EntityUtils.toString(httpEntity, "UTF-8");

}注意这里需要的是https的证书

2025-07-23 07:56:26