前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java中的签名和证书那些事

java中的签名和证书那些事

作者头像
山行AI
发布2019-06-28 16:23:35
1.5K0
发布2019-06-28 16:23:35
举报
文章被收录于专栏:山行AI山行AI

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
复制
 /**     *  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
复制
   public static boolean rsaCheckV1(Map<String, String> 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<String, String> 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<String, String> 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
复制
 /**     * 请求,只请求一次,不做重试     * @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.<ConnectionSocketFactory>create()                            .register("http", PlainConnectionSocketFactory.getSocketFactory())                            .register("https", sslConnectionSocketFactory)                            .build(),                    null,                    null,                    null            );        }        else {            connManager = new BasicHttpClientConnectionManager(                    RegistryBuilder.<ConnectionSocketFactory>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的证书

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-06-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开发架构二三事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • java中的签名和证书那些事
    • 1.数字签名
      • 2. 加密
        • 2.1 对称加密
        • 2.2 非对称加密
      • 3. 支付宝支付中的公钥与私钥
        • 3.1 私钥的处理
        • 3.2 公钥的处理:
      • 4. https的加密处理
      相关产品与服务
      SSL 证书
      腾讯云 SSL 证书(SSL Certificates)为您提供 SSL 证书的申请、管理、部署等服务,为您提供一站式 HTTPS 解决方案。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档