在java中调用不信任的https接口

如何在java中调用不安全的https接口

主要由两部分构成

  • 忽略 SSL 证书校验并声明协议为TLSv1.3
  • 禁用主机名验证
  • 下面的代码为Post实现,分别为传递body和传递表单包含文件。
  • java17使用的java.net,java8使用的javax.net

一个简单的分析方式

使用wireshark抓取对应的接口的日志,以及开启java关于tcp相关的日志打印。

java8

public String chatHttps(ChatRequest chatRequest) { URI uri = getUri("/api/v1/chat/completions"); URL uriUrl; try { uriUrl = uri.toURL(); } catch (MalformedURLException e) { throw new RuntimeException("获取接口失败", e); } log.info(uri.toString()); String body; try { body = objectMapper.writeValueAsString(chatRequest); } catch (IOException e) { throw new RuntimeException("传递信息失败", e); } // 忽略 SSL 证书校验 SSLContext sslContext = getSslContext(); HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); // 禁用主机名验证 HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true); // 打开连接并配置请求 HttpsURLConnection connection = getHttpsUrlConnection(uriUrl, body); StringBuilder response = new StringBuilder(); // 获取响应 try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) { String inputLine; while ((inputLine = in.readLine()) != null) { response.append(inputLine); } } catch (IOException e) { throw new RuntimeException("读取返回消息失败", e); } return response.toString(); } private URI getUri(String endPoint) { URI uri = URI.create(url); String basePath = uri.getPath().replaceAll("/+$", "") + endPoint; uri = uri.resolve(basePath); return uri; } private HttpsURLConnection getHttpsUrlConnection(URL uriUrl, String body) { RuntimeException connectionErrorMessage = null; HttpsURLConnection connection = null; int tryCount = 10; while (tryCount > 0) { try { connection = (HttpsURLConnection) uriUrl.openConnection(); connection.setRequestMethod("POST"); connection.setDoOutput(true); connection.setConnectTimeout((int) Duration.ofMinutes(2).toMillis()); connection.setReadTimeout((int) Duration.ofMinutes(2).toMillis()); connection.setRequestProperty("Authorization", "Bearer " + token); connection.setRequestProperty("Content-Type", "application/json"); // 写入请求体 try (OutputStream os = connection.getOutputStream()) { byte[] input = body.getBytes(StandardCharsets.UTF_8); os.write(input, 0, input.length); } break; } catch (IOException e) { connection = null; connectionErrorMessage = new RuntimeException("打开连接并配置请求失败", e); } tryCount--; } RuntimeException finalConnectionErrorMessage = connectionErrorMessage; Assert.notNull(connection, () -> finalConnectionErrorMessage); return connection; } private HttpsURLConnection getHttpsUrlConnection(URL uriUrl, MultipartFile file, Map<String, String> formFields) { RuntimeException connectionErrorMessage = null; HttpsURLConnection connection = null; // 生成唯一的boundary String boundary = "----WebKitFormBoundary" + UUID.randomUUID(); int tryCount = 10; while (tryCount > 0) { try { connection = (HttpsURLConnection) uriUrl.openConnection(); connection.setRequestMethod("POST"); connection.setDoOutput(true); connection.setConnectTimeout((int) Duration.ofMinutes(2).toMillis()); connection.setReadTimeout((int) Duration.ofMinutes(2).toMillis()); connection.setRequestProperty("Authorization", "Bearer " + token); connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); // 写入请求体 try (OutputStream os = connection.getOutputStream(); PrintWriter writer = new PrintWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8), true)) { // 写入普通表单字段 for (Map.Entry<String, String> entry : formFields.entrySet()) { String formFieldName = entry.getKey(); String formFieldValue = entry.getValue(); writer.append("--").append(boundary).append("\r\n"); writer.append("Content-Disposition: form-data; name=\"").append(formFieldName).append("\"").append("\r\n"); writer.append("Content-Type: text/plain; charset=").append(String.valueOf(StandardCharsets.UTF_8)).append("\r\n"); writer.append("\r\n"); writer.append(formFieldValue).append("\r\n"); writer.flush(); } // 写入文件的部分 String fileName = file.getOriginalFilename(); String contentType = file.getContentType(); long contentLength = file.getSize(); String encodedFileName = URLEncoder.encode(Objects.requireNonNull(fileName), UTF_8); writer.append("--").append(boundary).append("\r\n"); writer.append("Content-Disposition: form-data; name=\"file\"; filename=\"") .append(fileName) .append("\"; filename*=UTF-8''") .append(encodedFileName) // URL 编码的文件名 .append("\r\n"); writer.append("Content-Type: ").append(contentType).append("\r\n"); writer.append("Content-Length: ").append(String.valueOf(contentLength)).append("\r\n"); writer.append("\r\n"); writer.flush(); // 写入文件的头部信息 // 写入文件内容 try (InputStream fileInputStream = file.getInputStream()) { byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = fileInputStream.read(buffer)) != -1) { os.write(buffer, 0, bytesRead); } } // 写入结束边界 writer.append("\r\n").flush(); writer.append("--").append(boundary).append("--").append("\r\n"); } break; } catch (IOException e) { connection = null; connectionErrorMessage = new RuntimeException("打开连接并配置请求失败", e); } tryCount--; } RuntimeException finalConnectionErrorMessage = connectionErrorMessage; Assert.notNull(connection, () -> finalConnectionErrorMessage); return connection; } public String uploadFileHttps(MultipartFile file) { URI uri = getUri("/api/file/upload"); URL uriUrl; try { uriUrl = uri.toURL(); } catch (MalformedURLException e) { throw new RuntimeException("获取接口失败", e); } Map<String, String> map = new HashMap<>(3); map.put("bucketName", "chat"); map.put("metadata", "{}"); log.info(uri.toString()); SSLContext sslContext = getSslContext(); HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); // 禁用主机名验证 HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true); // 打开连接并配置请求 HttpsURLConnection connection = getHttpsUrlConnection(uriUrl, file, map); StringBuilder response = new StringBuilder(); // 获取响应 try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) { String inputLine; while ((inputLine = in.readLine()) != null) { response.append(inputLine); } } catch (IOException e) { throw new RuntimeException("读取返回消息失败", e); } return response.toString(); } private SSLContext getSslContext() { // 忽略 SSL 证书校验 SSLContext sslContext; try { sslContext = SSLContext.getInstance("TLSv1.3"); sslContext.init(null, new TrustManager[]{new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) { // 不校验客户端证书 } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) { // 不校验服务端证书 } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } }}, new SecureRandom()); } catch (NoSuchAlgorithmException | KeyManagementException e) { throw new RuntimeException("设置证书失败", e); } return sslContext; }

java17

public String chatHttps(ChatRequest chatRequest) { URI uri = getUri("/api/v1/chat/completions"); URL uriUrl; try { uriUrl = uri.toURL(); } catch (MalformedURLException e) { throw new RuntimeException("获取接口失败", e); } log.info(uri.toString()); String body; try { body = objectMapper.writeValueAsString(chatRequest); } catch (IOException e) { throw new RuntimeException("传递信息失败", e); } // 忽略 SSL 证书校验 SSLContext sslContext = getSslContext(); HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); // 禁用主机名验证 HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true); // 打开连接并配置请求 HttpsURLConnection connection = getHttpsUrlConnection(uriUrl, body); StringBuilder response = new StringBuilder(); // 获取响应 try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) { String inputLine; while ((inputLine = in.readLine()) != null) { response.append(inputLine); } } catch (IOException e) { throw new RuntimeException("读取返回消息失败", e); } return response.toString(); } private URI getUri(String endPoint) { URI uri = URI.create(url); String basePath = uri.getPath().replaceAll("/+$", "") + endPoint; uri = uri.resolve(basePath); return uri; } private HttpsURLConnection getHttpsUrlConnection(URL uriUrl, String body) { var ref = new Object() { RuntimeException connectionErrorMessage = null; }; HttpsURLConnection connection = null; int tryCount = 10; while (tryCount > 0) { try { connection = (HttpsURLConnection) uriUrl.openConnection(); connection.setRequestMethod("POST"); connection.setDoOutput(true); connection.setConnectTimeout((int) Duration.ofMinutes(2).toMillis()); connection.setReadTimeout((int) Duration.ofMinutes(2).toMillis()); connection.setRequestProperty("Authorization", "Bearer " + token); connection.setRequestProperty("Content-Type", "application/json"); // 写入请求体 try (OutputStream os = connection.getOutputStream()) { byte[] input = body.getBytes(StandardCharsets.UTF_8); os.write(input, 0, input.length); } break; } catch (IOException e) { connection = null; ref.connectionErrorMessage = new RuntimeException("打开连接并配置请求失败", e); } tryCount--; } Assert.notNull(connection, () -> ref.connectionErrorMessage); return connection; } private HttpsURLConnection getHttpsUrlConnection(URL uriUrl, MultipartFile file, Map<String, String> formFields) { var ref = new Object() { RuntimeException connectionErrorMessage = null; }; HttpsURLConnection connection = null; // 生成唯一的boundary String boundary = "----WebKitFormBoundary" + UUID.randomUUID(); int tryCount = 10; while (tryCount > 0) { try { connection = (HttpsURLConnection) uriUrl.openConnection(); connection.setRequestMethod("POST"); connection.setDoOutput(true); connection.setConnectTimeout((int) Duration.ofMinutes(2).toMillis()); connection.setReadTimeout((int) Duration.ofMinutes(2).toMillis()); connection.setRequestProperty("Authorization", "Bearer " + token); connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); // 写入请求体 try (OutputStream os = connection.getOutputStream(); PrintWriter writer = new PrintWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8), true)) { // 写入普通表单字段 for (Map.Entry<String, String> entry : formFields.entrySet()) { String formFieldName = entry.getKey(); String formFieldValue = entry.getValue(); writer.append("--").append(boundary).append("\r\n"); writer.append("Content-Disposition: form-data; name=\"").append(formFieldName).append("\"").append("\r\n"); writer.append("Content-Type: text/plain; charset=").append(String.valueOf(StandardCharsets.UTF_8)).append("\r\n"); writer.append("\r\n"); writer.append(formFieldValue).append("\r\n"); writer.flush(); } // 写入文件的部分 String fileName = file.getOriginalFilename(); String contentType = file.getContentType(); long contentLength = file.getSize(); String encodedFileName = URLEncoder.encode(Objects.requireNonNull(fileName), StandardCharsets.UTF_8); writer.append("--").append(boundary).append("\r\n"); writer.append("Content-Disposition: form-data; name=\"file\"; filename=\"") .append(fileName) .append("\"; filename*=UTF-8''") .append(encodedFileName) // URL 编码的文件名 .append("\r\n"); writer.append("Content-Type: ").append(contentType).append("\r\n"); writer.append("Content-Length: ").append(String.valueOf(contentLength)).append("\r\n"); writer.append("\r\n"); writer.flush(); // 写入文件的头部信息 // 写入文件内容 try (InputStream fileInputStream = file.getInputStream()) { byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = fileInputStream.read(buffer)) != -1) { os.write(buffer, 0, bytesRead); } } // 写入结束边界 writer.append("\r\n").flush(); writer.append("--").append(boundary).append("--").append("\r\n"); } break; } catch (IOException e) { connection = null; ref.connectionErrorMessage = new RuntimeException("打开连接并配置请求失败", e); } tryCount--; } Assert.notNull(connection, () -> ref.connectionErrorMessage); return connection; } public String uploadFileHttps(MultipartFile file) { URI uri = getUri("/api/file/upload"); URL uriUrl; try { uriUrl = uri.toURL(); } catch (MalformedURLException e) { throw new RuntimeException("获取接口失败", e); } Map<String, String> map = new HashMap<>(3); map.put("bucketName", "chat"); map.put("metadata", "{}"); log.info(uri.toString()); SSLContext sslContext = getSslContext(); HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); // 禁用主机名验证 HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true); // 打开连接并配置请求 HttpsURLConnection connection = getHttpsUrlConnection(uriUrl, file, map); StringBuilder response = new StringBuilder(); // 获取响应 try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) { String inputLine; while ((inputLine = in.readLine()) != null) { response.append(inputLine); } } catch (IOException e) { throw new RuntimeException("读取返回消息失败", e); } return response.toString(); } private SSLContext getSslContext() { // 忽略 SSL 证书校验 SSLContext sslContext; try { sslContext = SSLContext.getInstance("TLSv1.3"); sslContext.init(null, new TrustManager[]{new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) { // 不校验客户端证书 } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) { // 不校验服务端证书 } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } }}, new SecureRandom()); } catch (NoSuchAlgorithmException | KeyManagementException e) { throw new RuntimeException("设置证书失败", e); } return sslContext; }

__EOF__

本文作者CrossAutomaton
本文链接https://www.cnblogs.com/CrossAutomaton/p/18615085.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   CrossAutomaton  阅读(143)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
点击右上角即可分享
微信分享提示