欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

gsoap wcf https

程序员文章站 2022-07-14 17:06:42
...

客户端采用Gsoap 2.8.15 ,服务端WCF / web service

实现基于https 的web service通信,有完整代码配置(我的资源下载)


1. gsoap-2.8.15\gsoap\samples\ssl 下面是Gsoap自带完整例子,基本解决问题

客户端用户名密码校验:

	soap_ssl_init();
	soap_init(&soap);
	soap.userid = "michael";
	soap.passwd = "gaga";
	int result = soap_ssl_client_context(&soap,
	  /* SOAP_SSL_NO_AUTHENTICATION, */ /* for encryption w/o authentication */
	  /* SOAP_SSL_DEFAULT | SOAP_SSL_SKIP_HOST_CHECK, */	/* if we don't want the host name checks since these will change from machine to machine */
	  SOAP_SSL_NO_AUTHENTICATION,	/* use SOAP_SSL_DEFAULT in production code */
	  NULL, 		/* keyfile (cert+key): required only when client must authenticate to server (see SSL docs to create this file) */
	  NULL, 		/* password to read the keyfile */
	  NULL,	/* optional cacert file to store trusted certificates, use cacerts.pem for all public certificates issued by common CAs */
	  NULL,		/* optional capath to directory with trusted certificates */
	  NULL		/* if randfile!=NULL: use a file with random data to seed randomness */
	);
客户端证书校验:

	soap_ssl_init();
	soap_init(&soap);
	int result = soap_ssl_client_context(&soap,
	  /* SOAP_SSL_NO_AUTHENTICATION, */ /* for encryption w/o authentication */
	  /* SOAP_SSL_DEFAULT | SOAP_SSL_SKIP_HOST_CHECK, */	/* if we don't want the host name checks since these will change from machine to machine */
	  SOAP_SSL_REQUIRE_SERVER_AUTHENTICATION|SOAP_SSL_SKIP_HOST_CHECK,	/* use SOAP_SSL_DEFAULT in production code */
	  "client.pem", 		/* keyfile (cert+key): required only when client must authenticate to server (see SSL docs to create this file) */
	  "123456", 		/* password to read the keyfile */
	  "cacert.pem",	/* optional cacert file to store trusted certificates, use cacerts.pem for all public certificates issued by common CAs */
	  NULL,		/* optional capath to directory with trusted certificates */
	  NULL		/* if randfile!=NULL: use a file with random data to seed randomness */
	);

证书校验的时候,修改stdsoap2.c::ssl_verify_callback,直接返回1:(因为客户端发起https时,服务器首先返回自己证书和公钥,ssl_verify_callback选择是否信任,不信任的话立即导致失败)

ssl_verify_callback(int ok, X509_STORE_CTX *store)
{
#ifdef SOAP_DEBUG
  if (!ok)
  { char buf[1024];
    int err = X509_STORE_CTX_get_error(store);
    X509 *cert = X509_STORE_CTX_get_current_cert(store);
    fprintf(stderr, "SSL verify error or warning with certificate at depth %d: %s\n", X509_STORE_CTX_get_error_depth(store), X509_verify_cert_error_string(err));
    X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof(buf));
    fprintf(stderr, "certificate issuer %s\n", buf);
    X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
    fprintf(stderr, "certificate subject %s\n", buf);
    /* accept self signed certificates and certificates out of date */
    switch (err)
    { case X509_V_ERR_CERT_NOT_YET_VALID:
      case X509_V_ERR_CERT_HAS_EXPIRED:
      case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
      case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
        X509_STORE_CTX_set_error(store, X509_V_OK);
        ok = 1;
    }
  }
#endif
  /* Note: return 1 to continue, but unsafe progress will be terminated by OpenSSL */
  return 1;
}

补充soap.header, Action to

	memset(wsa5__Action, 0, sizeof(wsa5__Action));
	sprintf_s(wsa5__Action, 250, "%s%s", "http://tempuri.org/INMSServiceSSLV1/", funcname);

	header->wsa5__MessageID = wsa5__MessageID;
	header->wsa5__To = wsa5__To;
	header->wsa5__Action = wsa5__Action;
利用Gsoap / vs 生成代码:

:: vs 环境
call "D:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat
:: get wsdl
wsdl2h -s -c -o NMSServiceSSLV1.h http://localhost:19996/NMSServiceV1/?wsdl
::copy /b thewsdl.h+test.h NMSServiceSSLV1.h
:: change -I path
soapcpp2 -c -L -x NMSServiceSSLV1.h -I../../../;../../../import

gsoap wcf https
编译客户端需要openssl,下载OpenSSL-Win32,这里使用的是1.1.0g

http://slproweb.com/products/Win32OpenSSL.html

将OpenSSL-Win32\include 加入包含目录,将OpenSSL-Win32\lib加入库目录,window下面还要将libcrypto.lib/libssl.lib/libcrypto.lib等作为依赖库

WITH_OPENSSL加入编译选项

#define M_ASN1_STRING_data(x)   ((x)->data)

2. 服务器端服务非常简单

    [ServiceContract]
    interface INMSServiceSSLV1
    {
        [OperationContract]
        int Add(int a, int b);
    }
    public class NMSServiceSSLV1 : INMSServiceSSLV1
    {
		public int Add(int a, int b)
        {
            return a+b;
        }
    }
用户名密码app.config:

<?xml version="1.0"?>
<configuration>
  <system.net>
    <settings>
      <httpWebRequest useUnsafeHeaderParsing="true"/>
    </settings>
  </system.net>
  <system.runtime.remoting>
    <application>
      <channels>
        <channel ref="http" clientConnectionLimit="500">
          <clientProviders>
            <formatter ref="soap"/>
          </clientProviders>
        </channel>
        <channel ref="tcp" clientConnectionLimit="500">
          <clientProviders>
            <formatter ref="soap"/>
          </clientProviders>
        </channel>
      </channels>
    </application>
  </system.runtime.remoting>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="sslBehavior">
          <!--不提供通过浏览器输入https访问元数据的方式,必要时候关闭之-->
          <serviceMetadata httpsGetEnabled="true" httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="false"/>
          <!--服务器端提供证书-->
          <serviceCredentials>
            <clientCertificate>
              <authentication certificateValidationMode="None"/>
            </clientCertificate>
            <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="SSLSoap.CustomIdentityVerification,SSLSoap"/>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <wsHttpBinding>
        <binding name="has_security">
          <security mode="Transport">
            <!--采用传输安全,客户端凭证=Basic-->
            <transport clientCredentialType="Basic"/>
            <message clientCredentialType="None"/>
          </security>
        </binding>
        <binding name="no_security">
          <security mode="None">
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>

    <services>
      <service name="SSLSoap.NMSServiceSSLV1" behaviorConfiguration="sslBehavior">
        <endpoint address="" binding="wsHttpBinding" bindingConfiguration="has_security" contract="SSLSoap.INMSServiceSSLV1"/>
        <endpoint address="mexs" binding="mexHttpsBinding" contract="IMetadataExchange"/>
        <endpoint address="" binding="wsHttpBinding" bindingConfiguration="no_security" contract="SSLSoap.INMSServiceSSLV1"/>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
        <host>
          <baseAddresses>
            <!--基地址是https-->
            <add baseAddress="https://localhost:9996/NMSServiceSSLV1/"/>
            <!--基地址是http-->
            <add baseAddress="http://localhost:19996/NMSServiceV1/"/>            
          </baseAddresses>
        </host>
      </service>
    </services>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="false"/>
  </system.serviceModel>
</configuration>

证书app.config:

<?xml version="1.0"?>
<configuration>
  <system.net>
    <settings>
      <httpWebRequest useUnsafeHeaderParsing="true"/>
    </settings>
  </system.net>
  <system.runtime.remoting>
    <application>
      <channels>
        <channel ref="http" clientConnectionLimit="500">
          <clientProviders>
            <formatter ref="soap"/>
          </clientProviders>
        </channel>
        <channel ref="tcp" clientConnectionLimit="500">
          <clientProviders>
            <formatter ref="soap"/>
          </clientProviders>
        </channel>
      </channels>
    </application>
  </system.runtime.remoting>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="sslBehavior">
          <!--不提供通过浏览器输入https访问元数据的方式-->
          <serviceMetadata httpsGetEnabled="true" httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="false"/>
          <!--服务器端提供证书-->
          <serviceCredentials>
            <serviceCertificate storeName="My" x509FindType="FindBySubjectName" findValue="michael-xj" storeLocation="LocalMachine"/>
            <clientCertificate>
              <!--SSLSoap.CustomCertificateVerification,SSLSoap ,后面是程序集名称-->
              <authentication certificateValidationMode="Custom"  customCertificateValidatorType="SSLSoap.CustomCertificateVerification,SSLSoap"/>
            </clientCertificate>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <wsHttpBinding>
        <binding name="has_security">
          <security mode="Transport">
            <!--采用传输安全,客户端凭证=Basic-->
            <transport clientCredentialType="Certificate"/>
            <message clientCredentialType="None"/>
          </security>
        </binding>
        <binding name="no_security">
          <security mode="None">
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>

    <services>
      <service name="SSLSoap.NMSServiceSSLV1" behaviorConfiguration="sslBehavior">
        <endpoint address="" binding="wsHttpBinding" bindingConfiguration="has_security" contract="SSLSoap.INMSServiceSSLV1"/>
        <endpoint address="mexs" binding="mexHttpsBinding" contract="IMetadataExchange"/>
        <endpoint address="" binding="wsHttpBinding" bindingConfiguration="no_security" contract="SSLSoap.INMSServiceSSLV1"/>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
        <host>
          <baseAddresses>
            <!--基地址是https-->
            <add baseAddress="https://localhost:9996/NMSServiceSSLV1/"/>
            <!--基地址是http-->
            <add baseAddress="http://localhost:19996/NMSServiceV1/"/>            
          </baseAddresses>
        </host>
      </service>
    </services>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="false"/>
  </system.serviceModel>
</configuration>


3. 证书、证书端口绑定

以管理员权限运行cmd,制作测试证书

:: 创建根证书CA
:: .pvk 私钥文件;.cer 公钥文件 .pem base64编码秘钥 
makecert -n "CN=test" -r -sv testSSL.pvk testSSL.cer
makecert -n "CN=svr-test" -ic testSSL.cer -iv testSSL.pvk -sr LocalMachine -ss My -pe -sky exchange
:: 创建客户端证书
Makecert -n "CN=cli-test" -ic testSSL.cer -iv testSSL.pvk -sr CurrentUser -ss My -pe -sky exchange
:: 证书端口绑定,管理员权限
netsh 
http add sslcert ipport=0.0.0.0:9996 certhash=‎8a7741422a335160171f7656d7f277c1b84fd188 appid={af01c789-ce96-43c1-9789-4e0a9ab11dd0}
::Save encoded certificate to store failed => 0x5 (5) Failed,以管理员权限运行
mmc查看证书时将证书导出,然后用openssl命令将证书转换为pem等格式,用于客户端gsoap提交证书

:: pfx -> pem
openssl pkcs12 -in client.pfx -nodes -out client.pem
:: cer -> pem
openssl x509 -inform der -in certificate.cer -out certificate.pem

4. FAQ

::FAQ:
Could not establish trust relationship for the SSL/TLS secure channel with authority 'localhost:9996'.
ServicePointManager.ServerCertificateValidationCallback += RemoteCertificateValidate;

:: ssl_verify_callback / return 1 / 校验服务器返回证书
SOAP 1.2 fault: SOAP-ENV:Sender [no subcode]
"SSL_ERROR_SSL
error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed"
Detail: SSL_connect error in tcp_connect()

:: SSL_get_verify_result failed / SOAP_SSL_NO_AUTHENTICATION
SOAP 1.2 fault: SOAP-ENV:Sender [no subcode]
"unsupported certificate purpose"
Detail: SSL/TLS certificate presented by peer cannot be verified in tcp_connect()

:: 填充SOAP_ENV__Header
SOAP 1.2 fault: SOAP-ENV:Sender [wsa5:DestinationUnreachable]
"The message with To '' cannot be processed at the receiver, due to an AddressFilter mismatch at the EndpointDispatcher.  Check that the sender and receiver's EndpointAddresses agree."
Detail: [no detail]

:: wsHttpBinding <-> basicHttpBinding 相互修正
Detail: HTTP/1.1 415 Cannot process the message because the content type 'text/xml; charset=utf-8' was not the expected type 'application/soap+xml; charset=utf-8'.
Detail: HTTP/1.1 415 Cannot process the message because the content type 'application/soap+xml; charset=utf-8; action=""' was not the expected type 'text/xml; charset=utf-8'.

:: 检查客户端是否校验不通过,异常!
Error 403 fault: SOAP-ENV:Server [no subcode]
"HTTP Error"
Detail: HTTP/1.1 403 Forbidden

The HTTP request was forbidden with client authentication scheme 'Basic'.

:: netsh -> http add sslcert
:: 端口没有证书
SOAP 1.2 fault: SOAP-ENV:Sender [no subcode]
"Error observed by underlying BIO: No error"
Detail: SSL_connect error in tcp_connect()

An error occurred while making the HTTP request to https://192.168.1.5:9996/NMSServiceSSLV1/. This could be due to the fact that the server certificate is not configured properly with HTTP.SYS in the HTTPS case. This could also be caused by a mismatch of the security binding between the client and the server.