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
编译客户端需要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.
推荐阅读