无法从Java发送一个大小为48681字节的消息到安全的WCF服务

6
我需要从Java调用一个安全的WCF服务,并使用双向认证。所有东西都运行良好,除了我无法发送大于48680字节大小的消息。因此,成功发送了48680字节的消息,但是48681字节的消息没有发送成功,Java应用程序会出现读取超时异常,尽管WCF的配额设置允许发送更大的消息。
那么问题可能是什么?
编辑 源代码如下:
package foo.bar;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.junit.Test;

import javax.net.ssl.*;
import java.io.*;
import java.net.URL;
import java.security.KeyStore;

public class ReadTimedOutTest {

    @Test
    public void testReadTimedOut() throws Exception {
        URL url = new URL("https://services/endpoint/");

        HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
        setUpSSL(connection);
        connection.setDoOutput(true);
        connection.setDoInput(true);
        connection.setRequestMethod("POST");
        connection.setRequestProperty("SOAPAction", "http://namespace/2012/01/service/Operation");
        connection.setRequestProperty("Accept", "*/*");
        connection.setRequestProperty("Connection", "Keep-Alive");
        connection.setRequestProperty("Content-Type", "text/xml; charset=utf-8");

        connection.setConnectTimeout(10 * 1000);
        connection.setReadTimeout(10 * 1000);
        connection.setInstanceFollowRedirects(true);

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        InputStream is = ReadTimedOutTest.class.getResourceAsStream("payload.failure.xml");
        try {
            IOUtils.copy(is, bos);
        } finally {
            is.close();
        }
        byte[] bytes = bos.toByteArray();
        connection.setRequestProperty("Content-Length", String.valueOf(bytes.length));

        OutputStream os = connection.getOutputStream();
        try {
            IOUtils.copy(new ByteArrayInputStream(bytes), os);
            os.flush();
        } finally {
            os.close();
        }

        int respCode = connection.getResponseCode();
        if(respCode >= HttpsURLConnection.HTTP_INTERNAL_ERROR) {
            is = connection.getErrorStream();
            try {
                IOUtils.copy(is, System.err);
            } finally {
                is.close();
            }
        } else {
            is = connection.getInputStream();
            try {
                IOUtils.copy(is, System.out);
            } finally {
                is.close();
            }
        }
    }

    private void setUpSSL(HttpsURLConnection connection) throws Exception {
        byte[] bytes = FileUtils.readFileToByteArray(new File("d:\\workspace\\temp\\keystore"));
        KeyStore keyStore = KeyStore.getInstance("JKS");
        keyStore.load(new ByteArrayInputStream(bytes), "changeit".toCharArray());
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("X509");
        keyManagerFactory.init(keyStore, "changeit".toCharArray());

        bytes = FileUtils.readFileToByteArray(new File("d:\\workspace\\temp\\truststore"));
        KeyStore trustStore = KeyStore.getInstance("JKS");
        trustStore.load(new ByteArrayInputStream(bytes), "changeit".toCharArray());
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
        trustManagerFactory.init(trustStore);

        SSLContext context = SSLContext.getInstance("TLS");
        context.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
        SSLSocketFactory socketFactory = context.getSocketFactory();

        connection.setSSLSocketFactory(socketFactory);
    }

}

更新

我已经使用 .net WCF 客户端测试了该服务,并成功调用了该服务,所以我想知道问题出在哪里?为什么 WCF 客户端可以调用该服务,而即使使用普通的 HTTP POST 请求和 UrlConnection 的 Java 客户端却不能呢?


更新

这是一份 SOAP 消息样例:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Body>
        <DoSomethingUseful xmlns="http://namespace/2012/01/service">
        ...
        </DoSomethingUseful>
    </soap:Body>
</soap:Envelope>

更新

我被告知在 .net 服务端会出现 "需要客户端证书。请求中未找到证书" 的信息,似乎是在 ssl 会话恢复时发生的。当 Content-Length 大于 48680 字节时才会出现该情况。 此外,WCF 服务配置了基本的 Http 绑定来使用传输层安全性。


很不幸,这是我所在组织使用的外部服务。可能我会在周一之前获得配置。问题只发生在我尝试发送足够大的请求时。当接收到大响应时,没有任何问题。WCF服务的开发人员表示,在他们的内部网络中使用.NET客户端调用此服务没有任何问题。 - szhem
他们是否从其内部网络使用与您相同的端点? - Richard Blewett
1
@mijer,看起来客户端配置有误。你能给我们展示一下Java客户端代码吗? - Sergey Vyacheslavovich Brunov
1
为什么你只将结果固定在请求的大小上? - Jörg Beyer
2
由于问题似乎出现在客户端,您能否使用 Fiddler 捕获 Java 和 .NET 客户端的请求并进行比较(包括标头)? - Kiran Mothe
显示剩余10条评论
5个回答

1

你尝试过查看实际发送的数据包吗?例如使用tcpdump/Wireshark(http://www.wireshark.org/)?

我曾经在某些平台上遇到问题,无法访问一个位于非常挑剔的SSL卸载引擎后面的服务。


很遗憾,从Java方面来看没有什么特别的。交互过程如下:1. Java客户端向.NET服务发送_ack push_;2. 读取超时;3. Java客户端向.NET服务发送_ack fin_数据包;4. .NET服务回复_ack_。 - szhem
.NET客户端由服务的开发人员提供,所以我必须要求他们稍微修改一下。我会尽快告诉你结果。 - szhem
嗨,@anders。我已经发布了这个问题的答案。问题在于 TLS 重新协商。 - szhem

1

最终,我解决了这个问题。 问题如下:

  1. 给定的 .net 客户端的加密服务提供程序支持 tls 重新协商
  2. Java 客户端的加密提供程序不支持 tls 重新协商
  3. 在服务端没有设置 ClientCertNegotiation 选项,因此 Java 客户端无法重新协商。
  4. .net 服务没有响应等待数据的 Java 客户端,因为该服务期望客户端证书,但客户端无法提供它,因为不支持重新协商。

0

你问了什么可能是问题,这里有一些建议:

  • 你的请求大小。如果我理解正确,这基本上是你的假设。
  • 你的请求结构。
  • 服务器端策略,例如请求必须小于48681字节。在评论中,你告诉我们你正在使用一个不受你控制的外部服务器。
  • 服务器端错误。记住“不受你控制”的部分,并将其视为“有缺陷的”。
  • 竞争条件。

我建议尝试更广泛范围的请求。不要只是改变大小。


问题肯定不在配额上,因为.NET客户端能够发送更大的请求。我认为请求的结构不是问题,因为它是从.NET客户端的跟踪中获取的。正如我之前在主题中提到的,服务器端策略允许更大的请求。 - szhem

0

你尝试过使用SOAPUI工具发送请求并检查它是否表现相同吗?同时,值得注意的是下面的链接,它讨论了关于WCF服务和Java客户端的一些内容。

Java客户端和WCF服务


0

这可能是SOAP版本不匹配的问题。basichttpbinding的默认版本是SOAP 1.1。如果Java客户端使用的是SOAP 1.2,则会出现错误。

我们经常看到,当SOAP版本不匹配时,您收到的错误与实际错误无关。


Java使用soap 1.1。很抱歉这不太明显,但示例中的soap命名空间为http://schemas.xmlsoap.org/soap/envelope/,内容类型为text/xml - szhem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接