美文网首页
Java实现Https调用

Java实现Https调用

作者: lgtn | 来源:发表于2021-04-06 10:08 被阅读0次

    https的服务器配置见:https://www.jianshu.com/p/860a297e1323

    import java.security.cert.CertificateException;

    import java.security.cert.X509Certificate;

    import javax.net.ssl.X509TrustManager;

    import org.slf4j.Logger;

    import org.slf4j.LoggerFactory;

    import com.ctjsoft.ticket.https.example.util.CertificationUtil;

    /**

    * 证书认证管理,实现X509TrustManager接口

    */

    public class MyX509TrustManager implements X509TrustManager{

    private static final LoggerLOGGER = LoggerFactory.getLogger(MyX509TrustManager.class);

      private static final X509Certificate[]EMPTY_X509CERTIFICATE_ARRAY =new X509Certificate[]{};

      private X509Certificate[]rootCerts;

      public MyX509TrustManager(X509Certificate rootCertificate) {

    if(rootCertificate ==null) {

    throw new IllegalArgumentException("root certificate is null ");

          }

    LOGGER.info("init root certificate");

          this.rootCerts =new X509Certificate[]{rootCertificate};

      }

    public MyX509TrustManager(X509Certificate[] rootCerts) {

    if(rootCerts ==null || rootCerts.length ==0) {

    throw new IllegalArgumentException("root certificate is null ");

          }

    LOGGER.info("init root certificates ,sizei:{}",rootCerts.length);

          this.rootCerts = rootCerts;

      }

    @Override

      public void checkClientTrusted(X509Certificate[] arg0, String arg1)throws CertificateException {

    System.out.println("checkClientTrusted:"+arg1);

      }

    @Override

        public void checkServerTrusted(X509Certificate[] chain, String authType)throws CertificateException {

    if (chain ==null) {

    throw new CertificateException("checkServerTrusted: X509Certificate array is null");

            }

    if (chain.length <1) {

    throw new CertificateException("checkServerTrusted: X509Certificate is empty");

            }

    if (!(null != authType && authType.equals("ECDHE_RSA"))) {

    throw new CertificateException("checkServerTrusted: AuthType is not ECDHE_RSA");

            }

          //验证证书

           boolean isTrusted = CertificationUtil.verifyCertChain(chain,rootCerts);

          if(!isTrusted) {

    throw new CertificateException("server's Cert verify fail");

          }

    }

    @Override

      public X509Certificate[]getAcceptedIssuers() {

    return EMPTY_X509CERTIFICATE_ARRAY;

      }

    }

    import java.io.FileInputStream;

    import java.io.InputStream;

    import java.nio.file.Files;

    import java.nio.file.Path;

    import java.nio.file.Paths;

    import java.security.InvalidKeyException;

    import java.security.NoSuchAlgorithmException;

    import java.security.NoSuchProviderException;

    import java.security.Principal;

    import java.security.PublicKey;

    import java.security.SignatureException;

    import java.security.cert.CertificateException;

    import java.security.cert.CertificateExpiredException;

    import java.security.cert.CertificateFactory;

    import java.security.cert.CertificateNotYetValidException;

    import java.security.cert.X509Certificate;

    import java.util.ArrayList;

    import java.util.HashMap;

    import java.util.List;

    import java.util.Map;

    import org.slf4j.Logger;

    import org.slf4j.LoggerFactory;

    public class CertificationUtil {

    private static final LoggerLOGGER = LoggerFactory.getLogger(CertificationUtil.class);

      /**

    * 加载证书

        * @param inputStream

        * @return

        */

      public static X509CertificateloadCertificate(String certPath) {

    try  {

    Path path = Paths.get("src/main/resources", certPath);

                InputStream inputStream =new FileInputStream(path.toFile());

              CertificateFactory cf = CertificateFactory.getInstance("X509");

                  X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream);

                  cert.checkValidity();

              return cert;

            }catch (CertificateExpiredException e) {

    throw new RuntimeException("证书已过期", e);

          }catch (CertificateNotYetValidException e) {

    throw new RuntimeException("证书尚未生效", e);

          }catch (CertificateException e) {

    throw new RuntimeException("无效的证书", e);

          }catch (Exception e) {

    String errorMessage = e.getMessage() ==null ?"" : e.getMessage() +"。";

                if (certPath.startsWith("/")) {

    errorMessage +="ClassPath路径不可以/开头,请去除后重试。";

                }

    throw new RuntimeException("读取[" + certPath +"]失败。" + errorMessage, e);

            }

    }

    /**

    * 验证证书链是否是信任证书库中证书签发的

    *

        * @param certs    目标验证证书列表

        * @param rootCerts 可信根证书列表

        * @return 验证结果

    */

        public static boolean verifyCertChain(X509Certificate[] certs, X509Certificate[] rootCerts) {

    boolean sorted =sortByDn(certs);

            if (!sorted) {

    LOGGER.error("证书链验证失败:不是完整的证书链");

    return false;

            }

    //先验证第一个证书是不是信任库中证书签发的

            X509Certificate prev = certs[0];

            boolean firstOK =verifyCert(prev, rootCerts);

            if (!firstOK || certs.length ==1) {

    return firstOK;

            }

    //验证证书链

            for (int i =1; i < certs.length; i++) {

    try {

    X509Certificate cert = certs[i];

                    try {

    cert.checkValidity();

                    }catch (CertificateExpiredException e) {

    LOGGER.error("证书已经过期",e);

    return false;

                    }catch (CertificateNotYetValidException e) {

    LOGGER.error("证书未激活",e);

    return false;

                    }

    verifySignature(prev.getPublicKey(), cert);

                    prev = cert;

                }catch (Exception e) {

    LOGGER.error("证书链验证失败",e);

    return false;

                }

    }

    return true;

        }

    /**

    * 验证证书是否是信任证书库中证书签发的

    *

        * @param cert      目标验证证书

        * @param rootCerts 可信根证书列表

        * @return 验证结果

    */

        private static boolean verifyCert(X509Certificate cert, X509Certificate[] rootCerts) {

    try {

    cert.checkValidity();

            }catch (CertificateExpiredException e) {

    LOGGER.error("证书已经过期", e);

    return false;

            }catch (CertificateNotYetValidException e) {

    LOGGER.error("证书未激活", e);

    return false;

            }

    Map subjectMap =new HashMap();

            for (X509Certificate root : rootCerts) {

    subjectMap.put(root.getSubjectDN(), root);

            }

    Principal issuerDN = cert.getIssuerDN();

            X509Certificate issuer = subjectMap.get(issuerDN);

            if (issuer ==null) {

    LOGGER.error("证书链验证失败");

    return false;

            }

    try {

    PublicKey publicKey = issuer.getPublicKey();

                verifySignature(publicKey, cert);

            }catch (Exception e) {

    LOGGER.error("证书链验证失败", e);

    return false;

            }

    return true;

        }

    private static void verifySignature(PublicKey publicKey, X509Certificate cert)

    throws NoSuchProviderException, CertificateException, NoSuchAlgorithmException, InvalidKeyException,

                SignatureException {

    cert.verify(publicKey);

        }

    private static boolean sortByDn(X509Certificate[] certs) {

    //主题和证书的映射

            Map subjectMap =new HashMap();

            //签发者和证书的映射

            Map issuerMap =new HashMap();

            //是否包含自签名证书

            boolean hasSelfSignedCert =false;

            for (X509Certificate cert : certs) {

    if (isSelfSigned(cert)) {

    if (hasSelfSignedCert) {

    return false;

                    }

    hasSelfSignedCert =true;

                }

    Principal subjectDN = cert.getSubjectDN();

                Principal issuerDN = cert.getIssuerDN();

                subjectMap.put(subjectDN, cert);

                issuerMap.put(issuerDN, cert);

            }

    List certChain =new ArrayList();

            X509Certificate current = certs[0];

            addressingUp(subjectMap, certChain, current);

            addressingDown(issuerMap, certChain, current);

            //说明证书链不完整

            if (certs.length != certChain.size()) {

    return false;

            }

    //将证书链复制到原先的数据

            for (int i =0; i < certChain.size(); i++) {

    certs[i] = certChain.get(i);

            }

    return true;

        }

    /**

    * 向上构造证书链

    *

        * @param subjectMap 主题和证书的映射

        * @param certChain  证书链

        * @param current    当前需要插入证书链的证书,include

    */

        private static void addressingUp(final Map subjectMap, List certChain,

                                        final X509Certificate current) {

    certChain.add(0, current);

            if (isSelfSigned(current)) {

    return;

            }

    Principal issuerDN = current.getIssuerDN();

            X509Certificate issuer = subjectMap.get(issuerDN);

            if (issuer ==null) {

    return;

            }

    addressingUp(subjectMap, certChain, issuer);

        }

    /**

    * 向下构造证书链

    *

        * @param issuerMap 签发者和证书的映射

        * @param certChain 证书链

        * @param current  当前需要插入证书链的证书,exclude

    */

        private static void addressingDown(final Map issuerMap, List certChain,

                                          final X509Certificate current) {

    Principal subjectDN = current.getSubjectDN();

            X509Certificate subject = issuerMap.get(subjectDN);

            if (subject ==null) {

    return;

            }

    if (isSelfSigned(subject)) {

    return;

            }

    certChain.add(subject);

            addressingDown(issuerMap, certChain, subject);

        }

    /**

    * 验证证书是否是自签发的

    *

        * @param cert 目标证书

        * @return true;自签发,false;不是自签发

    */

        private static boolean isSelfSigned(X509Certificate cert) {

    return cert.getSubjectDN().equals(cert.getIssuerDN());

        }

    }


    static SSLSocketFactorygetSSLSocketFactory()throws NoSuchAlgorithmException, KeyManagementException {

    SSLContext ctx =null;

          ctx = SSLContext.getInstance("TLS");

          X509Certificate rootCertificate = CertificationUtil.loadCertificate("merrick2.crt");

          ctx.init(null, new TrustManager[] {new MyX509TrustManager(rootCertificate) }, new SecureRandom());

          return ctx.getSocketFactory();

      }

     一、JDK ---单向https

    private static String doPost(String httpUrl,String param) throws NoSuchAlgorithmException, KeyManagementException {

        HttpsURLConnection connection =null;

          InputStream is =null;

          OutputStream os =null;

          BufferedReader br =null;

            String result =null;

            try {

                URL url =new URL(httpUrl);

                // 通过远程url连接对象打开连接

                connection = (HttpsURLConnection) url.openConnection();

                connection.setSSLSocketFactory(getSSLSocketFactory());

                connection.setHostnameVerifier(new DefaultHostnameVerifier());

                // 设置连接请求方式

                connection.setRequestMethod(ExampleConstants.REQUEST_METHOD);

                // 设置连接主机服务器超时时间:60000毫秒

                connection.setConnectTimeout(2000);

                // 设置读取主机服务器返回数据超时时间:60000毫秒

                connection.setReadTimeout(60000);

                // 默认值为:false,当向远程服务器传送数据/写数据时,需要设置为true

                connection.setDoOutput(true);

                // 默认值为:true,当前向远程服务读取数据时,设置为true,该参数可有可无

                connection.setDoInput(true);

                // 设置传入参数的格式:请求参数应该是 name1=value1&name2=value2 的形式。

                connection.setRequestProperty("Content-Type", ExampleConstants.Content_Type);

                // 通过连接对象获取一个输出流

                os = connection.getOutputStream();

                // 通过输出流对象将参数写出去/传输出去,它是通过字节数组写出的

                os.write(param.getBytes());

                // 通过连接对象获取一个输入流,向远程读取

                if (connection.getResponseCode() ==200) {

                    is = connection.getInputStream();

                    // 对输入流对象进行包装:charset根据工作项目组的要求来设置

                    br =new BufferedReader(new InputStreamReader(is, ExampleConstants.DEFAULT_CHARSET));

                    StringBuffer sbf =new StringBuffer();

                    String temp =null;

                    // 循环遍历一行一行读取数据

                    while ((temp = br.readLine()) !=null) {

    sbf.append(temp);

                        sbf.append("\r\n");

                    }

    result = sbf.toString();

                }

    }catch (MalformedURLException e) {

    e.printStackTrace();

            }catch (IOException e) {

    e.printStackTrace();

            }finally {

    // 关闭资源

                if (null != br) {

    try {

    br.close();

                    }catch (IOException e) {

    e.printStackTrace();

                    }

    }

    if (null != os) {

    try {

    os.close();

                    }catch (IOException e) {

    e.printStackTrace();

                    }

    }

    if (null != is) {

    try {

    is.close();

                    }catch (IOException e) {

    e.printStackTrace();

                    }

    }

    // 断开与远程地址url的连接

                connection.disconnect();

            }

    return result;

        }

    二、OKhttp---单向https

    public static Response httpsPost(String url, String json)throws Exception {

    OkHttpClient client =new OkHttpClient.Builder().

    sslSocketFactory(getSSLSocketFactory()).

    //解决报错javax.net.ssl.SSLPeerUnverifiedException: Hostname 127.0.0.1 not verified

                        hostnameVerifier(new HostnameVerifier() {

    @Override

                    public boolean verify(String s, SSLSession sslSession) {

                    System.out.println("主机:" + s);

                    return true;

                    }

    }).

    connectTimeout(10, TimeUnit.MINUTES).

    readTimeout(10,TimeUnit.MINUTES).

    build();

        RequestBody body = RequestBody.create(JSON, json);

        Request request =new Request.Builder()

    .url(url)

    .post(body)

    .build();

        Response response = client.newCall(request).execute();

        return response;

    }

    三、双向认证

    如果要开启https双向认证:

    (1)nginx中增加配置:由nginx去对客户端证书的验证

    ssl_verify_client:on  # on-开启证书校验;off:关闭,此配置默认为关闭

    ssl_client_certificate:     #客户端 根级证书公钥所在路径

    (2)客户端代码改造:

    在单向https的代码基础上,增加客户端证书(pfx格式,包含公钥、私钥)

    第一种办法: MyX509TrustManager中传入两个证书

    第二种办法:MyX509TrustManager使用KeyStore,将证书放入KeyStore中,

    相关文章

      网友评论

          本文标题:Java实现Https调用

          本文链接:https://www.haomeiwen.com/subject/mhyqkltx.html