1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.wss4j.dom.message;
21
22 import java.time.ZoneOffset;
23 import java.time.ZonedDateTime;
24 import java.util.List;
25
26 import javax.crypto.KeyGenerator;
27 import javax.crypto.SecretKey;
28 import javax.security.auth.callback.CallbackHandler;
29
30 import org.apache.wss4j.common.WSEncryptionPart;
31 import org.apache.wss4j.common.crypto.Crypto;
32 import org.apache.wss4j.common.crypto.CryptoFactory;
33 import org.apache.wss4j.common.ext.WSSecurityException;
34 import org.apache.wss4j.common.saml.SAMLCallback;
35 import org.apache.wss4j.common.saml.SAMLUtil;
36 import org.apache.wss4j.common.saml.SamlAssertionWrapper;
37 import org.apache.wss4j.common.saml.builder.SAML1Constants;
38 import org.apache.wss4j.common.util.DateUtil;
39 import org.apache.wss4j.common.util.KeyUtils;
40 import org.apache.wss4j.common.util.SOAPUtil;
41 import org.apache.wss4j.common.util.XMLUtils;
42 import org.apache.wss4j.dom.WSConstants;
43 import org.apache.wss4j.dom.common.KeystoreCallbackHandler;
44 import org.apache.wss4j.dom.common.SAML1CallbackHandler;
45
46 import org.apache.wss4j.dom.engine.WSSConfig;
47 import org.apache.wss4j.dom.engine.WSSecurityEngine;
48 import org.apache.wss4j.dom.engine.WSSecurityEngineResult;
49 import org.apache.wss4j.dom.handler.WSHandlerResult;
50 import org.apache.wss4j.dom.saml.WSSecSignatureSAML;
51 import org.apache.wss4j.dom.util.SignatureUtils;
52 import org.apache.wss4j.dom.util.WSSecurityUtil;
53
54 import org.junit.jupiter.api.Test;
55 import org.w3c.dom.Document;
56 import org.w3c.dom.Element;
57 import org.w3c.dom.Node;
58
59 import static org.junit.jupiter.api.Assertions.assertTrue;
60 import static org.junit.jupiter.api.Assertions.fail;
61
62
63
64
65 public class ModifiedRequestTest {
66 private static final org.slf4j.Logger LOG =
67 org.slf4j.LoggerFactory.getLogger(ModifiedRequestTest.class);
68 private static final String SOAPMSG =
69 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
70 + "<SOAP-ENV:Envelope "
71 + "xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" "
72 + "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
73 + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
74 + "<SOAP-ENV:Body>"
75 + "<add xmlns=\"http://ws.apache.org/counter/counter_port_type\">"
76 + "<value xmlns=\"http://blah.com\">15</value>"
77 + "</add>"
78 + "</SOAP-ENV:Body>"
79 + "</SOAP-ENV:Envelope>";
80
81 private WSSecurityEngine secEngine = new WSSecurityEngine();
82 private CallbackHandler callbackHandler = new KeystoreCallbackHandler();
83 private Crypto crypto;
84
85 public ModifiedRequestTest() throws Exception {
86 WSSConfig.init();
87 crypto = CryptoFactory.getInstance();
88 }
89
90
91
92
93
94
95
96 @Test
97 public void testMovedElement() throws Exception {
98 Document doc = SOAPUtil.toSOAPPart(SOAPMSG);
99 WSSecHeader secHeader = new WSSecHeader(doc);
100 secHeader.insertSecurityHeader();
101
102 WSSecSignature builder = new WSSecSignature(secHeader);
103 builder.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
104 LOG.info("Before Signing....");
105
106 WSEncryptionPart encP =
107 new WSEncryptionPart(
108 "value",
109 "http://blah.com",
110 "");
111 builder.getParts().add(encP);
112
113 Document signedDoc = builder.build(crypto);
114
115
116
117
118
119 Element secHeaderElement = secHeader.getSecurityHeaderElement();
120 Element envelopeElement = signedDoc.getDocumentElement();
121 Node valueNode =
122 envelopeElement.getElementsByTagNameNS("http://blah.com", "value").item(0);
123 Node clonedValueNode = valueNode.cloneNode(true);
124 secHeaderElement.appendChild(clonedValueNode);
125 valueNode.getFirstChild().setNodeValue("250");
126
127 if (LOG.isDebugEnabled()) {
128 LOG.debug("After Signing....");
129 String outputString =
130 XMLUtils.prettyDocumentToString(signedDoc);
131 LOG.debug(outputString);
132 }
133
134 try {
135 verify(signedDoc);
136 fail("Failure expected on multiple elements with the same wsu:Id");
137 } catch (WSSecurityException ex) {
138 assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.FAILED_CHECK);
139 assertTrue(ex.getMessage().startsWith("javax.xml.crypto.URIReferenceException: " +
140 "org.apache.xml.security.utils.resolver.ResourceResolverException: "));
141 }
142 }
143
144
145
146
147
148
149
150
151 @Test
152 public void testMovedElementChangedId() throws Exception {
153 Document doc = SOAPUtil.toSOAPPart(SOAPMSG);
154 WSSecHeader secHeader = new WSSecHeader(doc);
155 secHeader.insertSecurityHeader();
156
157 WSSecSignature builder = new WSSecSignature(secHeader);
158 builder.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
159 LOG.info("Before Signing....");
160
161 WSEncryptionPart encP =
162 new WSEncryptionPart(
163 "value",
164 "http://blah.com",
165 "");
166 builder.getParts().add(encP);
167
168 Document signedDoc = builder.build(crypto);
169
170
171
172
173
174 Element secHeaderElement = secHeader.getSecurityHeaderElement();
175 Element envelopeElement = signedDoc.getDocumentElement();
176 Node valueNode =
177 envelopeElement.getElementsByTagNameNS("http://blah.com", "value").item(0);
178 Node clonedValueNode = valueNode.cloneNode(true);
179 secHeaderElement.appendChild(clonedValueNode);
180 valueNode.getFirstChild().setNodeValue("250");
181 ((Element)valueNode).setAttributeNS(
182 WSConstants.WSU_NS, "wsu:Id", "id-250"
183 );
184
185 if (LOG.isDebugEnabled()) {
186 LOG.debug("After Signing....");
187 String outputString =
188 XMLUtils.prettyDocumentToString(signedDoc);
189 LOG.debug(outputString);
190 }
191
192
193
194
195 WSHandlerResult results = verify(signedDoc);
196
197
198
199
200 envelopeElement = signedDoc.getDocumentElement();
201 Node bodyNode =
202 envelopeElement.getElementsByTagNameNS(
203 WSConstants.URI_SOAP11_ENV, "Body"
204 ).item(0);
205 valueNode =
206 ((Element)bodyNode).getElementsByTagNameNS(
207 "http://blah.com", "value"
208 ).item(0);
209
210 List<WSSecurityEngineResult> signedResults =
211 results.getActionResults().get(WSConstants.SIGN);
212 try {
213 SignatureUtils.verifySignedElement((Element)valueNode, signedResults);
214 fail("Failure expected on the required element not being signed");
215 } catch (WSSecurityException ex) {
216 assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.FAILED_CHECK);
217 }
218 }
219
220
221
222
223 @Test
224 public void testDuplicatedSignedSAMLAssertion() throws Exception {
225 SAML1CallbackHandler callbackHandler = new SAML1CallbackHandler();
226 callbackHandler.setStatement(SAML1CallbackHandler.Statement.AUTHN);
227 callbackHandler.setConfirmationMethod(SAML1Constants.CONF_SENDER_VOUCHES);
228 callbackHandler.setIssuer("www.example.com");
229
230 SAMLCallback samlCallback = new SAMLCallback();
231 SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
232 SamlAssertionWrapper samlAssertion = new SamlAssertionWrapper(samlCallback);
233
234 Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
235 WSSecHeader secHeader = new WSSecHeader(doc);
236 secHeader.insertSecurityHeader();
237
238 WSSecSignatureSAML wsSign = new WSSecSignatureSAML(secHeader);
239 wsSign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
240
241 Document signedDoc =
242 wsSign.build(
243 null, samlAssertion, crypto, "16c73ab6-b892-458f-abf5-2f875f74882e", "security"
244 );
245 Element assertionElement = (Element) samlAssertion.getElement().cloneNode(true);
246 assertionElement.removeChild(assertionElement.getFirstChild());
247 secHeader.getSecurityHeaderElement().appendChild(assertionElement);
248
249 if (LOG.isDebugEnabled()) {
250 LOG.debug("SAML 1.1 Authn Assertion (sender vouches):");
251 String outputString =
252 XMLUtils.prettyDocumentToString(signedDoc);
253 LOG.debug(outputString);
254 }
255
256 try {
257 verify(signedDoc);
258 fail("Failure expected on duplicate tokens");
259 } catch (WSSecurityException ex) {
260 assertTrue(ex.getMessage().contains(
261 "Multiple security tokens with the same Id have been detected"
262 ));
263 }
264 }
265
266
267
268
269 @Test
270 public void testDuplicatedSignedUsernameToken() throws Exception {
271 Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
272 WSSecHeader secHeader = new WSSecHeader(doc);
273 secHeader.insertSecurityHeader();
274
275 WSSecUsernameToken usernameToken = new WSSecUsernameToken(secHeader);
276 usernameToken.setUserInfo("wss86", "security");
277 usernameToken.build();
278
279 WSSecSignature builder = new WSSecSignature(secHeader);
280 builder.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
281
282 WSEncryptionPart encP =
283 new WSEncryptionPart(
284 "UsernameToken",
285 WSConstants.WSSE_NS,
286 "");
287 builder.getParts().add(encP);
288
289 builder.prepare(crypto);
290
291 List<javax.xml.crypto.dsig.Reference> referenceList =
292 builder.addReferencesToSign(builder.getParts());
293
294 builder.computeSignature(referenceList, false, null);
295
296 secHeader.getSecurityHeaderElement().appendChild(
297 usernameToken.getUsernameTokenElement().cloneNode(true)
298 );
299
300 if (LOG.isDebugEnabled()) {
301 LOG.debug("Signed Timestamp");
302 String outputString =
303 XMLUtils.prettyDocumentToString(doc);
304 LOG.debug(outputString);
305 }
306
307 try {
308 verify(doc);
309 fail("Failure expected on duplicate tokens");
310 } catch (WSSecurityException ex) {
311 assertTrue(ex.getMessage().contains(
312 "Multiple security tokens with the same Id have been detected"
313 ));
314 }
315 }
316
317
318
319
320 @Test
321 public void testModifiedEncryptedDataStructure() throws Exception {
322 Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
323 WSSecHeader secHeader = new WSSecHeader(doc);
324 secHeader.insertSecurityHeader();
325
326 WSSecEncrypt builder = new WSSecEncrypt(secHeader);
327 builder.setUserInfo("wss40");
328 builder.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
329 builder.setSymmetricEncAlgorithm(WSConstants.TRIPLE_DES);
330
331 Crypto wssCrypto = CryptoFactory.getInstance("wss40.properties");
332 KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.TRIPLE_DES);
333 SecretKey symmetricKey = keyGen.generateKey();
334 Document encryptedDoc = builder.build(wssCrypto, symmetricKey);
335
336 Element body = WSSecurityUtil.findBodyElement(doc);
337 Element encryptionMethod =
338 XMLUtils.findElement(body, "EncryptionMethod", WSConstants.ENC_NS);
339 encryptionMethod.setAttributeNS(null, "Algorithm", "http://new-algorithm");
340
341 String outputString =
342 XMLUtils.prettyDocumentToString(encryptedDoc);
343 if (LOG.isDebugEnabled()) {
344 LOG.debug(outputString);
345 }
346
347 WSSecurityEngine newEngine = new WSSecurityEngine();
348 try {
349 newEngine.processSecurityHeader(doc, null, new KeystoreCallbackHandler(), wssCrypto);
350 fail("Failure expected on a modified EncryptedData structure");
351 } catch (WSSecurityException ex) {
352 assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.INVALID_SECURITY);
353 }
354 }
355
356
357
358
359 @Test
360 public void testModifiedEncryptedDataCipherValue() throws Exception {
361 Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
362 WSSecHeader secHeader = new WSSecHeader(doc);
363 secHeader.insertSecurityHeader();
364
365 WSSecEncrypt builder = new WSSecEncrypt(secHeader);
366 builder.setUserInfo("wss40");
367 builder.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
368 builder.setSymmetricEncAlgorithm(WSConstants.TRIPLE_DES);
369
370 Crypto wssCrypto = CryptoFactory.getInstance("wss40.properties");
371 KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.TRIPLE_DES);
372 SecretKey symmetricKey = keyGen.generateKey();
373 Document encryptedDoc = builder.build(wssCrypto, symmetricKey);
374
375 Element body = WSSecurityUtil.findBodyElement(doc);
376 Element cipherValue =
377 XMLUtils.findElement(body, "CipherValue", WSConstants.ENC_NS);
378 String cipherText = cipherValue.getTextContent();
379
380 StringBuilder stringBuilder = new StringBuilder(cipherText);
381 int index = stringBuilder.length() / 2;
382 char ch = stringBuilder.charAt(index);
383 if (ch != 'A') {
384 ch = 'A';
385 } else {
386 ch = 'B';
387 }
388 stringBuilder.setCharAt(index, ch);
389 cipherValue.setTextContent(stringBuilder.toString());
390
391 String outputString =
392 XMLUtils.prettyDocumentToString(encryptedDoc);
393 if (LOG.isDebugEnabled()) {
394 LOG.debug(outputString);
395 }
396
397 WSSecurityEngine newEngine = new WSSecurityEngine();
398 try {
399 newEngine.processSecurityHeader(doc, null, new KeystoreCallbackHandler(), wssCrypto);
400 fail("Failure expected on a modified EncryptedData CipherValue");
401 } catch (WSSecurityException ex) {
402 assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.FAILED_CHECK);
403 }
404 }
405
406
407
408
409
410 @Test
411 public void testModifiedSecurityHeaderEncryptedDataCipherValue() throws Exception {
412 Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
413 WSSecHeader secHeader = new WSSecHeader(doc);
414 secHeader.insertSecurityHeader();
415
416 WSSecEncrypt builder = new WSSecEncrypt(secHeader);
417 builder.setUserInfo("wss40");
418 builder.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
419 builder.setSymmetricEncAlgorithm(WSConstants.TRIPLE_DES);
420
421 Crypto wssCrypto = CryptoFactory.getInstance("wss40.properties");
422
423 WSSecTimestamp timestamp = new WSSecTimestamp(secHeader);
424 timestamp.setTimeToLive(300);
425 timestamp.build();
426
427 WSEncryptionPart encP =
428 new WSEncryptionPart(
429 "Timestamp",
430 WSConstants.WSU_NS,
431 "");
432 builder.getParts().add(encP);
433
434 KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.TRIPLE_DES);
435 SecretKey symmetricKey = keyGen.generateKey();
436 Document encryptedDoc = builder.build(wssCrypto, symmetricKey);
437
438 Element securityHeader =
439 WSSecurityUtil.getSecurityHeader(encryptedDoc, "");
440 Element encryptedTimestamp =
441 XMLUtils.findElement(securityHeader, "EncryptedData", WSConstants.ENC_NS);
442 Element cipherValue =
443 XMLUtils.findElement(encryptedTimestamp, "CipherValue", WSConstants.ENC_NS);
444 String cipherText = cipherValue.getTextContent();
445
446 StringBuilder stringBuilder = new StringBuilder(cipherText);
447 int index = stringBuilder.length() / 2;
448 char ch = stringBuilder.charAt(index);
449 if (ch != 'A') {
450 ch = 'A';
451 } else {
452 ch = 'B';
453 }
454 stringBuilder.setCharAt(index, ch);
455 cipherValue.setTextContent(stringBuilder.toString());
456
457 String outputString =
458 XMLUtils.prettyDocumentToString(encryptedDoc);
459 if (LOG.isDebugEnabled()) {
460 LOG.debug(outputString);
461 }
462
463 WSSecurityEngine newEngine = new WSSecurityEngine();
464 try {
465 newEngine.processSecurityHeader(doc, null, new KeystoreCallbackHandler(), wssCrypto);
466 fail("Failure expected on a modified EncryptedData CipherValue");
467 } catch (WSSecurityException ex) {
468 assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.FAILED_CHECK);
469 }
470 }
471
472
473
474
475 @Test
476 public void testModifiedEncryptedKeyCipherValue() throws Exception {
477 Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
478 WSSecHeader secHeader = new WSSecHeader(doc);
479 secHeader.insertSecurityHeader();
480
481 WSSecEncrypt builder = new WSSecEncrypt(secHeader);
482 builder.setUserInfo("wss40");
483 builder.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
484 builder.setSymmetricEncAlgorithm(WSConstants.TRIPLE_DES);
485
486 Crypto wssCrypto = CryptoFactory.getInstance("wss40.properties");
487 KeyGenerator keyGen = KeyUtils.getKeyGenerator(WSConstants.TRIPLE_DES);
488 SecretKey symmetricKey = keyGen.generateKey();
489 Document encryptedDoc = builder.build(wssCrypto, symmetricKey);
490
491 Element encryptedKey =
492 XMLUtils.findElement(doc.getDocumentElement(), "EncryptedKey", WSConstants.ENC_NS);
493 Element cipherValue =
494 XMLUtils.findElement(encryptedKey, "CipherValue", WSConstants.ENC_NS);
495 String cipherText = cipherValue.getTextContent();
496
497 StringBuilder stringBuilder = new StringBuilder(cipherText);
498 int index = stringBuilder.length() / 2;
499 char ch = stringBuilder.charAt(index);
500 if (ch != 'A') {
501 ch = 'A';
502 } else {
503 ch = 'B';
504 }
505 stringBuilder.setCharAt(index, ch);
506 cipherValue.setTextContent(stringBuilder.toString());
507
508 String outputString =
509 XMLUtils.prettyDocumentToString(encryptedDoc);
510 if (LOG.isDebugEnabled()) {
511 LOG.debug(outputString);
512 }
513
514 WSSecurityEngine newEngine = new WSSecurityEngine();
515 try {
516 newEngine.processSecurityHeader(doc, null, new KeystoreCallbackHandler(), wssCrypto);
517 fail("Failure expected on a modified EncryptedData CipherValue");
518 } catch (WSSecurityException ex) {
519 assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.FAILED_CHECK);
520 }
521 }
522
523
524
525
526
527
528 @Test
529 public void testModifiedSignatureReference() throws Exception {
530 Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
531 WSSecHeader secHeader = new WSSecHeader(doc);
532 secHeader.insertSecurityHeader();
533
534 WSSecSignature builder = new WSSecSignature(secHeader);
535 builder.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
536
537 WSSecTimestamp timestamp = new WSSecTimestamp(secHeader);
538 timestamp.setTimeToLive(300);
539 timestamp.build();
540
541 WSEncryptionPart encP =
542 new WSEncryptionPart(
543 "Timestamp",
544 WSConstants.WSU_NS,
545 "");
546 builder.getParts().add(encP);
547
548 Document signedDoc = builder.build(crypto);
549
550
551 Element timestampElement = timestamp.getElement();
552 Element createdValue =
553 XMLUtils.findElement(timestampElement, "Created", WSConstants.WSU_NS);
554
555
556 ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC).plusSeconds(5L);
557 createdValue.setTextContent(DateUtil.getDateTimeFormatter(true).format(now));
558
559 if (LOG.isDebugEnabled()) {
560 String outputString =
561 XMLUtils.prettyDocumentToString(signedDoc);
562 LOG.debug(outputString);
563 }
564
565 try {
566 verify(signedDoc);
567 fail("Failure expected on a modified Signature Reference");
568 } catch (WSSecurityException ex) {
569 assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.FAILED_CHECK);
570 }
571 }
572
573
574
575
576 @Test
577 public void testUntrustedSignature() throws Exception {
578 Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
579 WSSecHeader secHeader = new WSSecHeader(doc);
580 secHeader.insertSecurityHeader();
581
582 WSSecSignature builder = new WSSecSignature(secHeader);
583 builder.setUserInfo("wss40", "security");
584
585 Crypto wss40Crypto = CryptoFactory.getInstance("wss40.properties");
586 Document signedDoc = builder.build(wss40Crypto);
587
588 if (LOG.isDebugEnabled()) {
589 String outputString =
590 XMLUtils.prettyDocumentToString(signedDoc);
591 LOG.debug(outputString);
592 }
593
594 try {
595 verify(signedDoc);
596 fail("Failure expected on an untrusted Certificate");
597 } catch (WSSecurityException ex) {
598 assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.FAILED_CHECK);
599 }
600 }
601
602
603
604
605 @Test
606 public void testModifiedSignature() throws Exception {
607 Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
608 WSSecHeader secHeader = new WSSecHeader(doc);
609 secHeader.insertSecurityHeader();
610
611 WSSecSignature builder = new WSSecSignature(secHeader);
612 builder.setUserInfo("16c73ab6-b892-458f-abf5-2f875f74882e", "security");
613
614 Document signedDoc = builder.build(crypto);
615
616
617 Element signatureElement = builder.getSignatureElement();
618 Node firstChild = signatureElement.getFirstChild();
619 while (!(firstChild instanceof Element) && firstChild != null) {
620 firstChild = signatureElement.getNextSibling();
621 }
622 ((Element)firstChild).setAttributeNS(null, "Id", "xyz");
623
624 if (LOG.isDebugEnabled()) {
625 String outputString =
626 XMLUtils.prettyDocumentToString(signedDoc);
627 LOG.debug(outputString);
628 }
629
630 try {
631 verify(signedDoc);
632 fail("Failure expected on a modified Signature element");
633 } catch (WSSecurityException ex) {
634 assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.FAILED_CHECK);
635 }
636 }
637
638
639
640
641
642
643
644
645 private WSHandlerResult verify(Document doc) throws Exception {
646 return secEngine.processSecurityHeader(doc, null, callbackHandler, crypto);
647 }
648
649 }