Web Services Security with Apache Rampart – Part 2 (Message-Level Security)

Apache Rampart is the Axis2 module that provides WS-Security functionality to Axis2 Web services and clients. Rampart currently implements WS-SOAP message security , WS-Security policy , WS-Secure conversation and WS-Trust specifications. In part one of this tutorial, we looked at applying transport-level security to a Web service and a client. In this tutorial, we will look at how to apply message-level security to a Web service and a client using Apache Rampart.

Date: Wed, 26th Mar, 2008
Level: Introductory
Reads: 50575 Comments: 31 | Login or register to post comments
Nandana Mihindukulasooriya
Tech Lead
WSO2 Inc.
 

Introduction

This tutorial is a step by step guide on how to sign a SOAP message with Apache Rampart using policy based configuration. We will also look at how to deploy the Apache Rampart module in Axis2. Although we have already covered deploying Rampart in Axis2, in part one of this tutorial, it is repeated here again for completeness sake only. Those who have already deployed Rampart can skip this section. We will then go and look at how a Axis2 Web service and a client can be secured with Rampart. As Axis2/Rampart has been proven to be highly interoperable, either the Web service or the client can still be written in some other Web service stack (including .NET / C / PHP) other than Axis2/Java.

See Web Services Security with Apache Rampart – Part 1 (Transport Level Security) for part I of this tutorial.

Applies To

<Project/lan> version
Apache Rampart 1.3
Apache Axis2 1.3

Table of Contents

Scenario

In this tutorial we will look at a scenario where SOAP messages will be signed. Signing SOAP messages ensures authentication, integrity and non-repudiation. As you could observe from the diagram given below a service and a client trusts each other, and have each other’s certificates in their key stores.

Scenario Image

We will use the Axis2 WAR deployed in Apache Tomcat server for this tutorial, as it is one of the most common use cases.

Deploying the Rampart Module

Deploying the module and necessary jars

If you have read part one of this tutorial and have successfully deployed Rampart in Axis2, then you can skip this section and jump directly to the section titled “Securing the Service”. 
Apache Rampart 1.3 binary distribution can be downloaded from here. Rampart distribution contains two module files, rampart-1.3 and rahas-1.3.mar . These module files should be copied to the modules directory of the Axis2 engine that can be found in TOMCAT_HOME/webapps/axis2/WEB-INF/modules, where TOMCAT_HOME is the home directory of the Apache Tomcat server in which Axis2 war is deployed. All the dependancy jars needed for Apache Rampart can be found under the libs directory of the Rampart distribution. These need to be copied to the lib directory of the Axis2 engine, which can found in TOMCAT_HOME/webapps/axis2/WEB-INF/lib.
You can check whether Apache Rampart is successfully deployed by logging in to Axis2 as the admin and using the System Components/available modules option in admin Web console . Both "rampart" and "rahas" should be listed under available modules, if you deploying of  rampart and rahas modules has been successfull.

Securing the Service

Step 1. Writing the Web service and the service descriptor

We will use the same simple service we used in part one of this tutorial for in this section as well. It has a single operation called "add" that adds two integers and returns the sum of them. We will be using the code first approach for this tutorial for simplicity. Service implementation class is given below.

package tutorial.rampart.service;

/**
 * Secure Service implementation class
 */
public class SecureService {
	
	public int add(int a, int b) {
		return a+b;
	}

}

Service descriptor for the above mentioned service is given below. You can find more information on how to write an Axis2 Web service in the tutorial titled "Hello world with Apache Axis2".

<service>
  <module ref="rampart"/>
  <parameter name="ServiceClass" locked="false">tutorial.rampart.service.SecureService</parameter>
  <operation name="add">
    <messageReceiver class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
  </operation>
</service>

Step 2. Setting up the key store

To sign messages back and forth, both the client and the service need to posses public-private key pairs. In this tutorial we will be using X509 certificates for both the client and the service. How to create key pairs and import them to key stores is out of the scope of this tutorial and you can find that information in another tutorial titled “Setting Up Keystores for a Client and a Service”. The service.jks which we use as the key store of the service, can be downloaded with the source code of this tutorial. You can use the keytool shipped with Java if you want inspect the keystore and see what keys it contains.

$ keytool -list -v -keystore path/to/service.jks -storepass servicePW 
 
Keystore type: jks
Keystore provider: SUN
 
Your keystore contains 2 entries
 
Alias name: service
Creation date: Mar 21, 2008
Entry type: keyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=service, OU=Secuirty Team, O=WSO2, L=colombo, ST=Western, C=LK, EMAILADDRESS=service@wso2.com
Issuer: CN=service, OU=Secuirty Team, O=WSO2, L=colombo, ST=Western, C=LK, EMAILADDRESS=service@wso2.com
Serial number: 47e3b6c0
Valid from: Fri Mar 21 18:53:12 LKT 2008 until: Tue Mar 15 18:53:12 LKT 2033
Certificate fingerprints:
         MD5:  C4:B9:2D:70:22:E9:08:6B:07:3B:2C:1E:5B:87:ED:09
         SHA1: 4F:C9:0C:42:01:B7:BE:AC:0D:4F:AC:00:A2:E7:CC:CA:07:40:8E:BB
 
 
*******************************************
*******************************************
 
 
Alias name: client
Creation date: Mar 21, 2008
Entry type: trustedCertEntry
 
Owner: CN=client, OU=Secuirty Team, O=WSO2, L=colombo, ST=Western, C=LK, EMAILADDRESS=client@wso2.com
Issuer: CN=client, OU=Secuirty Team, O=WSO2, L=colombo, ST=Western, C=LK, EMAILADDRESS=client@wso2.com
Serial number: 47e3b631
Valid from: Fri Mar 21 18:50:49 LKT 2008 until: Tue Mar 15 18:50:49 LKT 2033
Certificate fingerprints:
         MD5:  DE:66:EB:95:18:2E:44:97:05:CE:DF:FC:83:E9:53:C3
         SHA1: CE:E5:F0:BB:2F:46:A9:F0:45:60:4C:16:1B:33:FC:B5:09:0B:8C:13
 
 
*******************************************
*******************************************

As you can see, service.jks contains it’s public-private key and the public key of client as trusted certificates. Service needs it’s private key to sign the messages which is pretty obvious but why does it need the certificate of the client? It is because it needs the client’s public key to verify the signature of the client.

Step 3. Writing the password callback

In part one of this tutorial, we used a password callback for the service to authenticate the username tokens. You might be wondering why we need to write a password callback class for this scenario as we are not using username tokens. The use of password callback class here is different to its use in the previous tutorial. As you already know, we need the private key of the service to sign SOAP messages. Each private key has a password associated with it. In order to retrieve the private key, we need to provide the password of the relevant key and this password callback class is used for that purpose. The password callback class used in this tutorial is given below. Even though passwords are hard coded in this example, they can also be retrieved from a database, a LDAP server or any other storage by writing the relevant password retrieval logic in the password callback class.

package tutorial.rampart.service;

import org.apache.ws.security.WSPasswordCallback;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;

import java.io.IOException;

public class PWCBHandler implements CallbackHandler {

    public void handle(Callback[] callbacks) throws IOException,
            UnsupportedCallbackException {

        for (int i = 0; i < callbacks.length; i++) {
            
            // To use the private key to sign messages, we need to provide 
        	// the private key password 
            WSPasswordCallback pwcb = (WSPasswordCallback)callbacks[i];
               
            if(pwcb.getIdentifer().equals("service") ) {
            	pwcb.setPassword("servicePW");
                return;
            }         
        }
    }
}
Step 4. Constructing the security policy

We will be using the policy based configuration approach of Apache Rampart for this tutorial. So, we should construct a suitable security policy using WS-Security policy language to define the requirements of the Web service. The security policy used in this tutorial is given below. 

<wsp:Policy wsu:Id="SigOnly" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
  <wsp:ExactlyOne>
    <wsp:All>
      <sp:AsymmetricBinding xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
        <wsp:Policy>
          <sp:InitiatorToken>
            <wsp:Policy>
              <sp:X509Token sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/Never">
                <wsp:Policy>
                  <sp:WssX509V3Token10/>
                </wsp:Policy>
              </sp:X509Token>
            </wsp:Policy>
          </sp:InitiatorToken>
          <sp:RecipientToken>
            <wsp:Policy>
              <sp:X509Token sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/Never">
                <wsp:Policy>
                  <sp:WssX509V3Token10/>
                </wsp:Policy>
              </sp:X509Token>
            </wsp:Policy>
          </sp:RecipientToken>
          <sp:AlgorithmSuite>
            <wsp:Policy>
              <sp:TripleDesRsa15/>
            </wsp:Policy>
          </sp:AlgorithmSuite>
          <sp:Layout>
            <wsp:Policy>
              <sp:Strict/>
            </wsp:Policy>
          </sp:Layout>
          <sp:IncludeTimestamp/>
          <sp:OnlySignEntireHeadersAndBody/>
        </wsp:Policy>
      </sp:AsymmetricBinding>
      <sp:SignedParts xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
        <sp:Body/>
      </sp:SignedParts>
    </wsp:All>
  </wsp:ExactlyOne>
</wsp:Policy>

As you can see, the above security policy contains two main security assertions: an asymmetric binding assertion and a signed parts assertion. Asymmetric binding defines what keys to be used and a few additional properties such as which algorithms to be used in cryptographic operations, layout of the security header, etc. Signed parts assertion defines what parts of the message should be signed. In this tutorial we will be signing the SOAP body of the message. 
More information on WS-Security Policy language and how we can construct security policies to suit our security requirements can be found in the article titled ”Understanding WS – Security Policy Language”.

Step 5. Providing Rampart specific configuration details

Rampart uses a custom assertion called RampartConfig assertion to provide Rampart specific configuration details to Rampart Engine. RampartConfig for this tutorial is given below. 

<ramp:RampartConfig xmlns:ramp="http://ws.apache.org/rampart/policy">
    <ramp:user>service</ramp:user>
    <ramp:passwordCallbackClass>tutorial.rampart.service.PWCBHandler</ramp:passwordCallbackClass>
    <ramp:signatureCrypto>
        <ramp:crypto provider="org.apache.ws.security.components.crypto.Merlin">
            <ramp:property name="org.apache.ws.security.crypto.merlin.keystore.type">JKS</ramp:property>
            <ramp:property name="org.apache.ws.security.crypto.merlin.file">path/to/service.jks</ramp:property>
            <ramp:property name="org.apache.ws.security.crypto.merlin.keystore.password">servicePW</ramp:property>
        </ramp:crypto>
    </ramp:signatureCrypto>
</ramp:RampartConfig>

<ramp:user/> provides the alias of the key that should be used to sign the messages. As we have already seen in the listing of service.jks, private key alias of the service is “service”. <ramp:passwordCallbackClass> provides the password callback class which is used to retrieve the private key password. <ramp:signatureCrypto> carries information on the key store used to retrieve keys to create and verify signatures. These information include crypto provider, key store type, key store file and the key store password. (Note that this is the password of the key store and not the password of the private key).

Step 6. Engaging Rampart and applying the security policy

Now, we will look at how we can engage Rampart to the Web service and apply the security policy. This is done completly using the service descriptor. We don't have to modify the source of the Web service to secure it. First, we engage the Rampart module to the Web service adding  <module ref="rampart"/> element to the service descriptor. Then, we apply security by adding the policy to the service descriptor. Modified service descriptor after engaging Rampart and applying the policy is given below. Elements within the policy element are not shown for brevity. 

<service>
  <module ref="rampart"/>
  <parameter name="ServiceClass" locked="false">tutorial.rampart.service.SecureService</parameter>
  <operation name="add">
    <messageReceiver class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
  </operation>
  <wsp:Policy wsu:Id="UTOverTransport" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
    <wsp:ExactlyOne>
      <wsp:All>
        <sp:AsymmetricBinding xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy"> </sp:AsymmetricBinding>
        <sp:SignedParts xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy"></sp:SignedParts>
        <ramp:RampartConfig xmlns:ramp="http://ws.apache.org/rampart/policy"> </ramp:RampartConfig>
      <wsp:All>
    </wsp:ExactlyOne>
  </wsp:Policy>
</service>
Step 7. Deploy the service

Now we need to deploy this service in the Axis2 server. Create a service archive named SecureService.aar and drop it in to the services directory that can be found in TOMCAT_HOME/webapps/axis2/WEB-INF/services, where TOMCAT_HOME is the home of Apache Tomcat server.

Policy annotated WSDL

When a security policy is applied to a Web service, the WSDL will be annotated with that particular security policy so the client can secure the SOAP messages according to the policy defined in the WSDL. Code generators that generate Stubs to access the Web service can make use of these security polices defined in the WSDL. The policy annotated WSDL of the Web service we use for this tutorial can be found here.

Securing the Client

Step 1. Generating the Stub using WSDL2Java tool

Java2WSDL tool provided by Axis2 can be used to generate Stubs which can then be used to call Web services. Given below is the command we use in this tutorial to generate the Stub. -uri option is used to provide the URL of the WSDL, -p option is used to specify a custom package name for the generated code, -o option to specify a directory path for the generated code and -uw option to switch on un-wrapping. 

[Linux]
  $ sh WSDL2Java.sh -uri http://localhost:8080/axis2/services/SecureService?wsdl -p tutorial.rampart.client -uw  -o /project/path/
[Windows]
  $ WSDL2Java.bat -uri http://localhost:8080/axis2/services/SecureService?wsdl -p tutorial.rampart.client -uw -o /project/to/path/

Step 2. Writting the client

Now, we will write a client for the Web service using the Stub generated. Source code of the client is given below: 

package tutorial.rampart.client;

  public class SecureServiceCGClient {

    public static void main(String[] args) throws Exception {

      SecureServiceStub stub = new SecureServiceStub(null,"http://localhost:8080/axis2/services/SecureService");

      int a = 3;
      int b = 4;
      int result = stub.add(a, b);

      System.out.println(a + " + " + b + " = " + result);

    }

}
Step 3. Setting up the key store

As mentioned in "Securing the service", to sign the messages back and forth, the client also need to posses public-private key pair. In this tutorial we will be using X509 certificates for both client and the service. The client.jks which we use as the key store of the client, can downloaded with the source code of this tutorial. You can use the keytool which is shipped with Java if you want inspect the keystore and see what keys it contains.

$ keytool -v -list -keystore client.jks -storepass clientPW

Keystore type: jks
Keystore provider: SUN

Your keystore contains 2 entries

Alias name: service
Creation date: Mar 21, 2008
Entry type: trustedCertEntry

Owner: CN=service, OU=Secuirty Team, O=WSO2, L=colombo, ST=Western, C=LK, EMAILADDRESS=service@wso2.com
Issuer: CN=service, OU=Secuirty Team, O=WSO2, L=colombo, ST=Western, C=LK, EMAILADDRESS=service@wso2.com
Serial number: 47e3b6c0
Valid from: Fri Mar 21 18:53:12 LKT 2008 until: Tue Mar 15 18:53:12 LKT 2033
Certificate fingerprints:
         MD5:  C4:B9:2D:70:22:E9:08:6B:07:3B:2C:1E:5B:87:ED:09
         SHA1: 4F:C9:0C:42:01:B7:BE:AC:0D:4F:AC:00:A2:E7:CC:CA:07:40:8E:BB


*******************************************
*******************************************


Alias name: client
Creation date: Mar 21, 2008
Entry type: keyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=client, OU=Secuirty Team, O=WSO2, L=colombo, ST=Western, C=LK, EMAILADDRESS=client@wso2.com
Issuer: CN=client, OU=Secuirty Team, O=WSO2, L=colombo, ST=Western, C=LK, EMAILADDRESS=client@wso2.com
Serial number: 47e3b631
Valid from: Fri Mar 21 18:50:49 LKT 2008 until: Tue Mar 15 18:50:49 LKT 2033
Certificate fingerprints:
         MD5:  DE:66:EB:95:18:2E:44:97:05:CE:DF:FC:83:E9:53:C3
         SHA1: CE:E5:F0:BB:2F:46:A9:F0:45:60:4C:16:1B:33:FC:B5:09:0B:8C:13


*******************************************
*******************************************

As you can see, this key store contains the client's public-private key pair as a keyEntry and the service's public key as trusted certificate. Client's private key will be used to sign messages sent to the client and service's public key will be used to verify the validity of the signature in messages coming from the service.

Step 4. Writing the password callback

This is similar to the password callback class we wrote for the service. Purpose of this password callback class is to provide the password for the client’s private key. 

package tutorial.rampart.client;

import org.apache.ws.security.WSPasswordCallback;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;

import java.io.IOException;

public class PWCBHandler implements CallbackHandler {

    public void handle(Callback[] callbacks) throws IOException,
            UnsupportedCallbackException {

        for (int i = 0; i < callbacks.length; i++) {
            
            // To use the private key to sign messages, we need to provide 
        	// the private key password 
            WSPasswordCallback pwcb = (WSPasswordCallback)callbacks[i];
               
            if(pwcb.getIdentifer().equals("client") ) {
            	pwcb.setPassword("clientPW");
                return;
            }
           
        }
    }

}
Step 5. Engaging Rampart

To secure SOAP requests made by the client, we need to engage the Rampart module to the client. For this, we need to create a client repository and rampart.mar should be deployed in the modules directory. We need to ensure that all dependency jar files of the Apache Rampart module is in the classpath of the client. Then the following code can be used to engage Rampart to the client: 

  // Rampart module should be in the repository 
  ConfigurationContext ctx = ConfigurationContextFactory.createConfigurationContextFromFileSystem("path/to/client/repo", null);

  SecureServiceStub stub = new SecureServiceStub(ctx,"https://localhost:8080/axis2/services/SecureService");

  ServiceClient sc = stub._getServiceClient();
  sc.engageModule("rampart");

Step 6. Setting the Rampart specific configuration details

Axis2 code generator makes use of security policies specified in the WSDL, when generating a stub for a Web service. We need to provide some Rampart specific configuration information such as which key to be used for the signature and which key store to be used. In this tutorial, we provide these information using a programmatically created RampartConfig assertion.   

  RampartConfig rampartConfig = new RampartConfig();
  rampartConfig.setUser("client");
  rampartConfig.setPwCbClass("tutorial.rampart.client.PWCBHandler");

  CryptoConfig sigCrypto = new CryptoConfig();
  sigCrypto.setProvider("org.apache.ws.security.components.crypto.Merlin");

  Properties props = new Properties();
  props.setProperty("org.apache.ws.security.crypto.merlin.keystore.type", "JKS");
  props.setProperty("org.apache.ws.security.crypto.merlin.file","keys/client.jks");
  props.setProperty("org.apache.ws.security.crypto.merlin.keystore.password", "clientPW");

  sigCrypto.setProp(props);

  rampartConfig.setSigCryptoConfig(sigCrypto);

  Policy policy = new Policy();
  policy.addAssertion(rampartConfig);

And we can add this policy to the service client using the following code.

  Policy rampartConfig = getRampartConfig();
  sc.getAxisService().getPolicyInclude().addPolicyElement(PolicyInclude.AXIS_SERVICE_POLICY, rampartConfig);
Step 7. Calling the Web service

Now, everything is set and you can run the client and consume the Web service.

SOAP Messages Exchanged

Without security
<soapenv:Envelope>
        <soapenv:Body>
                <ns1:add xmlns:ns1="http://service.rampart.tutorial">
                        <ns1:a>4</ns1:a>
                        <ns1:b>6</ns1:b>
                </ns1:add>
        </soapenv:Body>
</soapenv:Envelope>
With Security
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
	<soapenv:Header>
        	<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soapenv:mustUnderstand="true">
			<wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="Timestamp-13121387">
				<wsu:Created>2008-03-27T15:29:37.454Z</wsu:Created>
				<wsu:Expires>2008-03-27T15:34:37.454Z</wsu:Expires>
			</wsu:Timestamp>
			<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="Signature-29744585">
				<ds:SignedInfo>
					<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
					<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
					<ds:Reference URI="#Id-14293164">
						<ds:Transforms>
							<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
						</ds:Transforms>
						<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
						<ds:DigestValue>KELVaFQ7RnfPIUMAU9q4D/5rGOU=</ds:DigestValue>
					</ds:Reference>
					<ds:Reference URI="#Timestamp-13121387">
						<ds:Transforms>
							<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
						</ds:Transforms>
						<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
						<ds:DigestValue>7t9QUVXRJ0yTS+84OSfsH7pAguM=</ds:DigestValue>
					</ds:Reference>
				</ds:SignedInfo>
				<ds:SignatureValue> ...ZL1FMFxsUvwBU2ZYYbNxGu/uJceG1i4uSPd6+BSiqYWal ...</ds:SignatureValue>
				<ds:KeyInfo Id="KeyId-24374386">
					<wsse:SecurityTokenReference xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="STRId-8406772">
						<wsse:KeyIdentifier EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" 
                                                                    ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509SubjectKeyIdentifier">
                                                        ins6410Q1skpvizn19AUk7dC6rI=
                                                </wsse:KeyIdentifier>
					</wsse:SecurityTokenReference>
				</ds:KeyInfo>
		       </ds:Signature>
	       </wsse:Security>
      </soapenv:Header>
      <soapenv:Body xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="Id-14293164">
      	<ns1:add xmlns:ns1="http://service.rampart.tutorial">
            <ns1:a>3</ns1:a>
            <ns1:b>4</ns1:b>
         </ns1:add>
      </soapenv:Body>
</soapenv:Envelope>

Summary

In this tutorial, we looked at deploying Apache Rampart module and applying message-level security to a Web service. We also looked at consuming a secure Web service. Even though in this example, we only  looked at a simple sign only scenario, we can sign and encrypt SOAP headers and also other parts of a message, just by changing the security policy by adding necessary protection assertions.

References

   1. Web Services Security with Apache Rampart – Part 1 (Transport Level Security)
   2. Setting Up Keystores for a Client and a Service
   3. Understanding the WS Security Policy Language

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值