java中的签名和证书那些事

2019-06-28 16:23:35 浏览数 (1)

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的证书

0 人点赞