You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

HTTPS请求报错SSLException: Received fatal alert: internal_error

问题背景

调用API发送HTTPS POST请求时,执行connection.getOutputStream()获取输出流环节触发错误,同一段代码在HTTP协议场景下可正常运行。
代码实现逻辑:初始化URL连接后,根据目标地址协议自动选择HTTP/HTTPS连接通道,配置请求头、超时时间等参数后写入请求体,最后读取响应内容拼接返回。
对应实现代码如下:

public String sendNativePost(String httpURL, Map headers, String body, String respType)
throws Exception {

    URL url = new URL(httpURL);
    HttpURLConnection connection;

    if (url.getProtocol().toLowerCase().equals("https")) {
        url = new URL(null, httpURL, new sun.net.www.protocol.https.Handler());
        trustAllHosts();
        HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
        https.setHostnameVerifier(DO_NOT_VERIFY);
        connection = https;
    } else {
        connection = (HttpURLConnection) url.openConnection();
    }

    connection.setRequestMethod("POST");
    connection.setRequestProperty("Accept", "*/*");

    if (headers != null) {
        for (Object key : headers.keySet()) {
            connection.setRequestProperty((String) key, (String) headers.get(key));
        }
    } else {
        logger.error("Header is Empty");
    }

    connection.setConnectTimeout(10 * 1000);
    connection.setReadTimeout(10 * 1000);
    connection.setDoOutput(true);
    connection.setDoInput(true);
    connection.setRequestProperty(
        "User-Agent", SMSServer.APPLICATION_NAME + "_" + SMSServer.APPLICATION_VERSION);
    try (OutputStream os = connection.getOutputStream()) {
        byte[] input = body.getBytes(StandardCharsets.UTF_8);
        logger.debug("get body-");
        os.write(input, 0, input.length);
        logger.debug("write body");
    } catch (Exception e) {
        logger.error("Output Stream Error!", e);
    }

    BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
    String responseMessage = "";

    while (true) {
        String line = br.readLine();
        if (line != null) {
            responseMessage += line;
        } else {
            break;
        }
    }

    br.close();
    connection.disconnect();

    return responseMessage;
}
报错信息

代码执行时抛出SSL异常,日志如下:

2022-06-13 17:17:41:323 ERROR HttpClient:224 - Output Stream Error!
javax.net.ssl.SSLException: Received fatal alert: internal_error
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:208)
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:154)
    at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:2023)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1125)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
    at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
    at sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(HttpURLConnection.java:1316)
    at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1291)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:250)
    at smsclient.HTTPClient.sendNativePost(HTTPClient.java:217)
    at smsclient.SMSClientHttp.sendPOST(SMSClientHttp.java:256)
    at smsclient.SMSClientHttp.sendSMS(SMSClientHttp.java:147)
    at smsclient.SMSClientHttp.processSMS(SMSClientHttp.java:42)
    at smsclient.HTTPClientMessageSender.run(HTTPClientMessageSender.java:75)
问题定位与修复

从报错栈可以看出,异常发生在SSL初始握手阶段,调用getOutputStream()时会隐式触发连接建立和SSL握手,还没执行到写请求体的逻辑,和输出流本身无关,是HTTPS握手被服务端拒绝导致的。

根因

  • 代码依赖JDK内部私有API:直接实例化sun.net.www.protocol.https.Handler、重复初始化URL对象的操作属于非标准用法,不同JDK版本、厂商的内部实现存在差异,很容易导致SSL上下文初始化异常。
  • TLS版本不匹配:从报错栈的行号判断当前运行环境是JDK8早期版本,默认启用的TLS版本为1.0/1.1,而当前绝大多数公网HTTPS API已经强制要求TLS1.2及以上版本,客户端发起握手时使用的版本过低,服务端直接返回internal_error断开连接,这是该报错最常见的诱因。
  • 自定义信任逻辑存在缺陷:普遍流传的trustAllHosts()实现大多没有正确初始化SSL上下文,也没有显式指定支持的加密套件,容易导致握手失败。
  • 代码存在异常吞掉的问题:输出流获取/写入的异常被catch后仅打日志,没有终止执行,后续还会继续调用getInputStream(),会抛出二次异常掩盖真实根因。

修复方案

  1. 移除所有依赖JDK内部私有API的逻辑,不需要重复实例化URL对象,删除new URL(null, httpURL, new sun.net.www.protocol.https.Handler())这段冗余代码。
  2. 正确初始化SSL上下文,显式指定使用TLSv1.2版本,测试环境可临时配置信任所有证书跳过校验,生产环境必须将服务端证书导入信任库使用正规校验逻辑:
// 全局初始化一次即可,不需要每次请求都执行
private static void initSSLContext() throws Exception {
    TrustManager[] trustAllCerts = new TrustManager[]{
        new X509TrustManager() {
            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
            @Override
            public void checkClientTrusted(X509Certificate[] certs, String authType) {}
            @Override
            public void checkServerTrusted(X509Certificate[] certs, String authType) {}
        }
    };
    // 强制指定TLS版本为1.2,适配服务端要求
    SSLContext sc = SSLContext.getInstance("TLSv1.2");
    sc.init(null, trustAllCerts, new SecureRandom());
    HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    // 以下主机名校验跳过逻辑仅测试环境使用,生产环境需移除
    HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);
}
  1. 修正HTTPS连接分支逻辑:
URL url = new URL(httpURL);
HttpURLConnection connection;
if (url.getProtocol().toLowerCase().equals("https")) {
    initSSLContext();
    connection = (HttpsURLConnection) url.openConnection();
} else {
    connection = (HttpURLConnection) url.openConnection();
}
  1. 修复其他代码缺陷:
    • 输出流操作的catch块不要吞异常,打印日志后直接抛出,避免后续执行无意义的代码掩盖问题。
    • 响应内容拼接改用StringBuilder,不要在循环里用+=拼接字符串,避免不必要的性能损耗。
    • 所有流、连接的关闭逻辑统一放在try-with-resources或finally块中,避免异常场景下连接泄漏。

内容的提问来源于stack exchange,提问作者Md Alif Al Amin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 17:57:19