@@ -38,43 +38,209 @@ $transport = Psr18Transport::createForClient(
38
38
39
39
### WsseMiddleware
40
40
41
- If you ever had to implement Web Service Security (WSS / WSSE) manually, you know that it is a lot of work to get this one working.
42
- Luckily for you we created an opinionated WSSE middleware that can be used to sign your SOAP requests.
41
+ Oh boy ... WS-Security ... can be a real pain !
42
+ This package aims for being as flexible as possible and provides you the tools you need to correctly configure Web Service Security.
43
+ The components are shaped based on the [ WS-Security UI inside SoapUI] ( https://www.soapui.org/docs/soapui-projects/ws-security/ ) .
44
+ This enables you to configure everything the way your SOAP server wants you to!
45
+ If you have a working config on SoapUI, you can transform it to PHP code by following the entries and their configurations.
46
+
47
+ * Usage:*
43
48
44
- ** Usage**
45
49
``` php
46
50
use Http\Client\Common\PluginClient;
47
51
use Soap\Psr18Transport\Psr18Transport;
48
52
use Soap\Psr18WsseMiddleware\WsseMiddleware;
49
53
50
- // Simple:
51
- $wsse = new WsseMiddleware('privatekey.pem', 'publickey.pyb');
54
+ $transport = Psr18Transport::createForClient(
55
+ new PluginClient($yourPsr18Client, [
56
+ new WsseMiddleware([$entries])
57
+ ])
58
+ );
59
+ ```
52
60
53
- // With signed headers. E.g: in combination with WSA:
54
- $wsse = (new WsseMiddleware('privatekey.pem', 'publickey.pyb'))
55
- ->withAllHeadersSigned();
61
+ The WSSE middleware can be built out of multiple configurable entries:
56
62
57
- // With configurable timestamp expiration:
58
- $wsse = (new WsseMiddleware('privatekey.pem', 'publickey.pyb'))
59
- ->withTimestamp(3600);
63
+ * BinarySecurityToken
64
+ * Decryption
65
+ * Encryption
66
+ * SamlAssertion
67
+ * Signature
68
+ * Timestamp
69
+ * Username
60
70
61
- // With plain user token:
62
- $wsse = (new WsseMiddleware('privatekey.pem', 'publickey.pyb'))
63
- ->withUserToken('username', 'password', false);
71
+ Underneath, there are some common examples on how to configure the ` $wsseMiddleware ` .
64
72
65
- // With digest user token:
66
- $wsse = (new WsseMiddleware('privatekey.pem', 'publickey.pyb'))
67
- ->withUserToken('username', 'password', true);
73
+ #### Adding a username and password
68
74
69
- // With end-to-end encryption enabled:
70
- $wsse = (new WsseMiddleware('privatekey.pem', 'publickey.pyb'))
71
- ->withEncryption('client-x509.pem')
72
- ->withServerCertificateHasSubjectKeyIdentifier(true);
75
+ Some services require you to add a username and optionally a password.
76
+ This can be done with following middleware.
73
77
74
- // Configure your PSR18 client:
75
- $transport = Psr18Transport::createForClient(
76
- new PluginClient($yourPsr18Client, [
77
- $wsse
78
- ])
78
+ ``` php
79
+ use Soap\Psr18WsseMiddleware\WsseMiddleware;
80
+ use Soap\Psr18WsseMiddleware\WSSecurity\Entry;
81
+
82
+ $wsseMiddleware = new WsseMiddleware(
83
+ outgoing: [
84
+ (new Entry\Username($user))
85
+ ->withPassword('xxx')
86
+ ->withDigest(false),
87
+ ]
79
88
);
80
89
```
90
+
91
+ #### Signing a SOAP request with PKCS12 or X509 certificate.
92
+
93
+ This is one of the most common implementation of WSS out there.
94
+ You are granted a certificate by the soap service with which you need to fetch data.
95
+
96
+ In case of a p12 certificate: convert it to a private key and public X509 certificate first:
97
+
98
+ ``` bash
99
+ openssl pkcs12 -in your.p12 -out security_token.pub -clcerts -nokeys
100
+ openssl pkcs12 -in your.p12 -out security_token.priv -nocerts -nodes
101
+ ```
102
+
103
+ Next, you can configure the middleware like this:
104
+
105
+ ``` php
106
+ use Soap\Psr18WsseMiddleware\WSSecurity\KeyStore\Certificate;
107
+ use Soap\Psr18WsseMiddleware\WSSecurity\KeyStore\Key;
108
+ use Soap\Psr18WsseMiddleware\WSSecurity\SignatureMethod;
109
+ use Soap\Psr18WsseMiddleware\WSSecurity\DigestMethod;
110
+ use Soap\Psr18WsseMiddleware\WSSecurity\KeyIdentifier;
111
+ use Soap\Psr18WsseMiddleware\WsseMiddleware;
112
+ use Soap\Psr18WsseMiddleware\WSSecurity\Entry;
113
+
114
+ $privKey = Key::fromFile('security_token.priv')->withPassphrase('xxx'); // Regular private key (not wrapped in X509)
115
+ $pubKey = Certificate::fromFile('security_token.pub'); // Public X509 cert
116
+
117
+ $wsseMiddleware = new WsseMiddleware(
118
+ outgoing: [
119
+ new Entry\Timestamp(60),
120
+ new Entry\BinarySecurityToken($pubKey),
121
+ (new Entry\Signature(
122
+ $privKey,
123
+ new KeyIdentifier\BinarySecurityTokenIdentifier()
124
+ ))
125
+ ->withSignatureMethod(SignatureMethod::RSA_SHA256)
126
+ ->withDigestMethod(DigestMethod::SHA256)
127
+ ->withSignAllHeaders(true)
128
+ ->withSignBody(true)
129
+ ]
130
+ );
131
+ ```
132
+
133
+ This example can also be used in combination with signing and username authentication.
134
+
135
+ #### Authorize a SOAP request with a SAML assertion
136
+
137
+ Another common implementation is authentication through a WS-Trust compliant STS instance.
138
+ In this case, you first have to fetch a SAML assertion from the STS service.
139
+ Most of them require you to sign the request with a X509 certificate.
140
+ This can be done with the middleware above.
141
+
142
+ Once you received back your SAML assertion, you have to pass it to the webservice you want to contact.
143
+ A common configuration for passing the SAML assertion might look like this:
144
+
145
+ ``` php
146
+ use Soap\Psr18WsseMiddleware\WSSecurity\KeyStore\Certificate;
147
+ use Soap\Psr18WsseMiddleware\WSSecurity\KeyStore\Key;
148
+ use Soap\Psr18WsseMiddleware\WSSecurity\SignatureMethod;
149
+ use Soap\Psr18WsseMiddleware\WSSecurity\DigestMethod;
150
+ use Soap\Psr18WsseMiddleware\WSSecurity\KeyIdentifier;
151
+ use Soap\Psr18WsseMiddleware\WsseMiddleware;
152
+ use Soap\Psr18WsseMiddleware\WSSecurity\Entry;
153
+ use VeeWee\Xml\Dom\Document;
154
+ use function VeeWee\Xml\Dom\Locator\document_element;
155
+
156
+ $privKey = Key::fromFile('security_token.priv')->withPassphrase('xxx'); // Regular private key (not wrapped in X509)
157
+
158
+ // These are provided through the STS service.
159
+ $samlAssertion = Document::fromXmlString(<<<EOXML
160
+ <saml:Assertion xmlns:saml =" urn:oasis:names:tc:SAML:1.0:assertion" AssertionID =" xxxx" />
161
+ EOXML
162
+ );
163
+ $samlAssertionId = $samlAssertion->locate(document_element())->getAttribute('AssertionID');
164
+
165
+ $wsseMiddleware = new WsseMiddleware(
166
+ outgoing: [
167
+ new Entry\Timestamp(60),
168
+ (new Entry\Signature(
169
+ $privKey,
170
+ new KeyIdentifier\SamlKeyIdentifier($samlAssertionId)
171
+ ))
172
+ ->withSignatureMethod(SignatureMethod::RSA_SHA256)
173
+ ->withDigestMethod(DigestMethod::SHA256)
174
+ ->withSignAllHeaders(true)
175
+ ->withSignBody(true)
176
+ ->withInsertBefore(false),
177
+ new Entry\SamlAssertion($samlAssertion),
178
+ ]
179
+ );
180
+ ```
181
+
182
+ #### Encrypt sensitive data
183
+
184
+ Some services require you to encrypt sensitive parts of the request and decrypt sensitive parts of the response.
185
+ In this case, you can add your public key to the request, encrypt the payload and send it over the wire.
186
+ Incoming responses will be encrypted with your public key and kan be decrypted by using your private key.
187
+
188
+
189
+ Encryption contains a [ known bug] ( https://github.com/robrichards/wse-php/pull/67 ) in the underlying [ robrichards/wse-php] ( https://github.com/robrichards/wse-php ) library.
190
+ Since a fix has not been merged yet, you can apply a patch like this:
191
+
192
+ ``` bash
193
+ composer require --dev cweagans/composer-patches
194
+ ```
195
+
196
+ ``` json
197
+ {
198
+ "extra" : {
199
+ "patches" : {
200
+ "robrichards/wse-php" : {
201
+ "Fix encryption bug" : " https://patch-diff.githubusercontent.com/raw/robrichards/wse-php/pull/67.diff"
202
+ }
203
+ }
204
+ }
205
+ }
206
+ ```
207
+
208
+ The configuration for encryption looks like this:
209
+
210
+ ``` php
211
+ use Soap\Psr18WsseMiddleware\WSSecurity\DataEncryptionMethod;
212
+ use Soap\Psr18WsseMiddleware\WSSecurity\KeyEncryptionMethod;
213
+ use Soap\Psr18WsseMiddleware\WSSecurity\KeyStore\Certificate;
214
+ use Soap\Psr18WsseMiddleware\WSSecurity\KeyStore\Key;
215
+ use Soap\Psr18WsseMiddleware\WSSecurity\SignatureMethod;
216
+ use Soap\Psr18WsseMiddleware\WSSecurity\DigestMethod;
217
+ use Soap\Psr18WsseMiddleware\WSSecurity\KeyIdentifier;
218
+ use Soap\Psr18WsseMiddleware\WsseMiddleware;
219
+ use Soap\Psr18WsseMiddleware\WSSecurity\Entry;
220
+
221
+ $privKey = Key::fromFile('security_token.priv')->withPassphrase('xxx'); // Regular private key (not wrapped in X509)
222
+ $pubKey = Certificate::fromFile('security_token.pub'); // Public X509 cert
223
+ $signKey = Certificate::fromFile('sign-key.pem'); // X509 cert for signing. Could be the same as $pubKey.
224
+
225
+ $wsseMiddleware = new WsseMiddleware(
226
+ outgoing: [
227
+ new Entry\Timestamp(60),
228
+ new Entry\BinarySecurityToken($pubKey),
229
+ (new Entry\Signature(
230
+ $privKey,
231
+ new KeyIdentifier\BinarySecurityTokenIdentifier()
232
+ ))
233
+ (new Entry\Encryption(
234
+ $signKey,
235
+ new KeyIdentifier\X509SubjectKeyIdentifier($signKey)
236
+ ))
237
+ ->withKeyEncryptionMethod(KeyEncryptionMethod::RSA_OAEP_MGF1P)
238
+ ->withDataEncryptionMethod(DataEncryptionMethod::AES256_CBC)
239
+ ],
240
+ incoming: [
241
+ new Entry\Decryption($privKey)
242
+ ]
243
+ );
244
+ ```
245
+
246
+ Note: Encryption only can also be done without adding a signature.
0 commit comments