Skip to content

Commit c2fd1d6

Browse files
committed
Fix #23. Handle EncryptedAssertion that contains a EncryptedData that contains a KeyInfo with a reference, not the EncryptedKey to be used
1 parent dcc0b23 commit c2fd1d6

4 files changed

Lines changed: 51 additions & 3 deletions

File tree

core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ public boolean isValid(String requestId) {
162162

163163
String responseTag = "{" + Constants.NS_SAMLP + "}Response";
164164
String assertionTag = "{" + Constants.NS_SAML + "}Assertion";
165-
165+
166166
final boolean hasSignedResponse = signedElements.contains(responseTag);
167167
final boolean hasSignedAssertion = signedElements.contains(assertionTag);
168168

@@ -741,7 +741,7 @@ public ArrayList<String> processSignedElements() throws Exception {
741741
for (int i = 0; i < signNodes.getLength(); i++) {
742742
Node signNode = signNodes.item(i);
743743
String signedElement = "{" + signNode.getParentNode().getNamespaceURI() + "}" + signNode.getParentNode().getLocalName();
744-
744+
745745
String responseTag = "{" + Constants.NS_SAMLP + "}Response";
746746
String assertionTag = "{" + Constants.NS_SAML + "}Assertion";
747747

@@ -817,7 +817,7 @@ public boolean validateSignedElements(ArrayList<String> signedElements) throws E
817817

818818
String responseTag = "{" + Constants.NS_SAMLP + "}Response";
819819
String assertionTag = "{" + Constants.NS_SAML + "}Assertion";
820-
820+
821821
if ((occurrences.containsKey(responseTag) && occurrences.get(responseTag) > 1)
822822
|| (occurrences.containsKey(assertionTag) && occurrences.get(assertionTag) > 1)
823823
|| !occurrences.containsKey(responseTag) && !occurrences.containsKey(assertionTag)) {

core/src/main/java/com/onelogin/saml2/util/Util.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import org.apache.commons.codec.binary.Base64;
5555
import org.apache.commons.codec.digest.DigestUtils;
5656
import org.apache.commons.lang3.StringUtils;
57+
import org.apache.xml.security.encryption.CipherData;
5758
import org.apache.xml.security.encryption.EncryptedData;
5859
import org.apache.xml.security.encryption.EncryptedKey;
5960
import org.apache.xml.security.encryption.XMLCipher;
@@ -899,6 +900,36 @@ public static void decryptElement(Element encryptedDataElement, PrivateKey input
899900

900901
XMLCipher xmlCipher = XMLCipher.getInstance();
901902
xmlCipher.init(XMLCipher.DECRYPT_MODE, null);
903+
904+
/* Check if we have encryptedData with a KeyInfo that contains a RetrievalMethod to obtain the EncryptedKey.
905+
xmlCipher is not able to handle that so we move the EncryptedKey inside the KeyInfo element and
906+
replacing the RetrievalMethod.
907+
*/
908+
909+
NodeList keyInfoInEncData = encryptedDataElement.getElementsByTagNameNS(Constants.NS_DS, "KeyInfo");
910+
if (keyInfoInEncData.getLength() == 0) {
911+
throw new Exception("No KeyInfo inside EncryptedData element");
912+
}
913+
914+
NodeList childs = keyInfoInEncData.item(0).getChildNodes();
915+
for (int i=0; i < childs.getLength(); i++) {
916+
if (childs.item(i).getLocalName() != null && childs.item(i).getLocalName().equals("RetrievalMethod")) {
917+
Element retrievalMethodElem = (Element)childs.item(i);
918+
if (!retrievalMethodElem.getAttribute("Type").equals("http://www.w3.org/2001/04/xmlenc#EncryptedKey")) {
919+
throw new Exception("Unsupported Retrieval Method found");
920+
}
921+
922+
String uri = retrievalMethodElem.getAttribute("URI").substring(1);
923+
924+
NodeList encryptedKeyNodes = ((Element) encryptedDataElement.getParentNode()).getElementsByTagNameNS(Constants.NS_XENC, "EncryptedKey");
925+
for (int j=0; j < encryptedKeyNodes.getLength(); j++) {
926+
if (((Element)encryptedKeyNodes.item(j)).getAttribute("Id").equals(uri)) {
927+
keyInfoInEncData.item(0).replaceChild(encryptedKeyNodes.item(j), childs.item(i));
928+
}
929+
}
930+
}
931+
}
932+
902933
xmlCipher.setKEK(inputKey);
903934
xmlCipher.doFinal(encryptedDataElement.getOwnerDocument(), encryptedDataElement, false);
904935
} catch (Exception e) {

core/src/test/java/com/onelogin/saml2/test/authn/AuthnResponseTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,22 @@ public void testGetNameIdData() throws Exception {
243243
assertTrue(samlResponse.getNameIdData().isEmpty());
244244
}
245245

246+
/**
247+
* Tests the decryptAssertion method of SamlResponse
248+
* Case: EncryptedAssertion with an encryptedData element with a KeyInfo
249+
* that contains a RetrievalMethod to obtain the EncryptedKey.
250+
*
251+
* @throws Exception
252+
*/
253+
@Test
254+
public void testEncryptedResponse() throws Exception {
255+
Saml2Settings settings = new SettingsBuilder().fromFile("config/config.decrypt.properties").build();
256+
257+
String samlResponseEncoded = Util.getFileAsString("data/responses/response_to_decrypt.xml.base64");
258+
SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded));
259+
assertEquals("archit.neema@intellicus.com", samlResponse.getNameId());
260+
}
261+
246262
/**
247263
* Tests the getNameIdData method of SamlResponse
248264
* Case: No NameId
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBEZXN0aW5hdGlvbj0iaHR0cDovLzE3Mi4yNi40My44MTo4MDgwL2phdmEtc2FtbC1qc3BzYW1wbGUvYWNzLmpzcCIgSUQ9ImlkNDU1MzY5NDA2NTI0OTY0NDIwNzI4MDI1NDkiIEluUmVzcG9uc2VUbz0iT05FTE9HSU5fY2I5ZGM0M2EtMTMzMy00YWRlLThmNmQtOWViYzZiMzcyMWM2IiBJc3N1ZUluc3RhbnQ9IjIwMTYtMTAtMDRUMTA6NDI6NTIuMTI5WiIgVmVyc2lvbj0iMi4wIj48c2FtbDI6SXNzdWVyIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OmVudGl0eSI+aHR0cDovL3d3dy5va3RhLmNvbS9leGtwaGV4N3hlb3dLaDh6TDF0Njwvc2FtbDI6SXNzdWVyPjxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZHNpZy1tb3JlI3JzYS1zaGEyNTYiLz48ZHM6UmVmZXJlbmNlIFVSST0iI2lkNDU1MzY5NDA2NTI0OTY0NDIwNzI4MDI1NDkiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L2RzOlRyYW5zZm9ybXM+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI3NoYTI1NiIvPjxkczpEaWdlc3RWYWx1ZT5hME9oTmdTYXl2eUpBcSs2dklMOVN3eDA4K1VSUzRnaXl2UUhaSHFaMWtvPTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT5rWGVUMWp0YzVuNENtSXlwemFQUk9ZUU43Nnk2YkJCZGk2ZTFuczMydjN5K3ovczBkY1AyYW5MbUdOTzU4SlMxV2U4T0pUVjBQQkh5b2JGTFpOT0hEY3BWckptNThLMGJXa3dEVCtZZktlaUcxbHp0WWpIb0NRTDViRkphbkt6SFRtd3R4SzR4WnR3RXMvdk85RmJ3S2NqdXJmbTB0YTFTbGNHQnVXK25MZWtKR3Z4R3E2cUQ1TlhEMXI2ZWM1dGZmRE5ZVjR4M2twRGFhaG5JYVFBME1QcGNsdVgyZXoxM2VpTXpCWmVFU1hEUkEyK3RQRFp4TkYzRE1lTlViYlNlMU5VN3VzMjJ5b2dmSGVwWWpqTDQ5OVVtUTRWQXd4SmlQei9wcXU0bDcyOExib1pnMkZoSWZTZnRTYjlBT3phNmtlVStaZ0oxWnpQb3B1UTFoeTV1d0E9PTwvZHM6U2lnbmF0dXJlVmFsdWU+PGRzOktleUluZm8+PGRzOlg1MDlEYXRhPjxkczpYNTA5Q2VydGlmaWNhdGU+TUlJRHJqQ0NBcGFnQXdJQkFnSUdBVmRmczhSOU1BMEdDU3FHU0liM0RRRUJCUVVBTUlHWE1Rc3dDUVlEVlFRR0V3SlZVekVUTUJFRw0KQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVXTUJRR0ExVUVCd3dOVTJGdUlFWnlZVzVqYVhOamJ6RU5NQXNHQTFVRUNnd0VUMnQwWVRFVQ0KTUJJR0ExVUVDd3dMVTFOUFVISnZkbWxrWlhJeEdEQVdCZ05WQkFNTUQybHVkR1ZzYkdsamRYTnBibVJwWVRFY01Cb0dDU3FHU0liMw0KRFFFSkFSWU5hVzVtYjBCdmEzUmhMbU52YlRBZUZ3MHhOakE1TWpVd05EVTFNemxhRncweU5qQTVNalV3TkRVMk16bGFNSUdYTVFzdw0KQ1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0F3S1EyRnNhV1p2Y201cFlURVdNQlFHQTFVRUJ3d05VMkZ1SUVaeVlXNWphWE5qYnpFTg0KTUFzR0ExVUVDZ3dFVDJ0MFlURVVNQklHQTFVRUN3d0xVMU5QVUhKdmRtbGtaWEl4R0RBV0JnTlZCQU1NRDJsdWRHVnNiR2xqZFhOcA0KYm1ScFlURWNNQm9HQ1NxR1NJYjNEUUVKQVJZTmFXNW1iMEJ2YTNSaExtTnZiVENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUA0KQURDQ0FRb0NnZ0VCQUtoYlpab2xKeVpISHBTd1NiaERiY3JmT3pzSWlqRnJVdXZmTXRYZVpRdWtwNnRHSXpzRHFPUW03SzdyQ0ZVdg0KdUFQRlBVbEd2NnIxOTFmZTNUUkg2dDNWT2tjdEpFZ2ZGR1RPd0JQSVV3L3o2bkZiK3ZENHljYXdINklNaC9iRDZyaTYwbkhsdVZzag0KY3pqMXR3YTY4T3JLTUFNNVJDRVIxMFZHTGhxdWRzR0VQT2tGUDVoaUNTUEdaTHJZeXFnVVNESlZ2dFJwaGRRTFZJdVhJQkVDNzdEMg0KZVNZbjlxRzhNYXFYZVZadkQ3MGFvOVVlQkZJajJzVFhiWENpUUxtUElSZ0VUQTdqcXNWVU1Fcnc3TXBpV2ZzbnAvUEU3ajVqS2ttaQ0KM2lJcjRnUW5Cd2xyT3ZyWFE5UVJHaGh6NkxlMkFEbkVvVzZoeVpzK0VRV3pOb3JNNWVjQ0F3RUFBVEFOQmdrcWhraUc5dzBCQVFVRg0KQUFPQ0FRRUFpVVhjYVB1WU5TLzFyMDM0aWhSdXUyZGlhNGZpcEJNQ2VhU2ExVU4wOUs3VXN6cDdibkZqakpCaUJybExrVmtmc0E2aw0KYndRcnlIT2hQVVg2YVBRbWhjeTNGWTlXQko4dHYxRlBDVzBXVTBLSDJ3NXRxa1VmU0ZLMWhsV3N1dHN0Y2xmWnFZVVY3emRNbzk5Ng0KMTF5MGlQUk1NZ2pBREJlWE84WWZlSFd1dENCbnpndlJQWkRKZ0pyR1RWakEvOElPZGRETEtiZUk5TS9RQ0FTTHgzZ2t5dnVKWEk0dg0KeGpKTUF2clVIdFdlMVRtU0xYSTQvZEdHb1hmb3ZyMmlBVzlGOS9wWDlwR3EyQ2tJam5MaFFtc2tBaVJVdFFwblN0OGQyTVVuSlBkKw0KQnJ4Y0U2T2ROVTdHWE01TnJ2VjZZOWlmc0FaWXBPOUxLVWtGQ0cxT3RwN0NnZz09PC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWwycDpTdGF0dXMgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiPjxzYW1sMnA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1sMnA6U3RhdHVzPjxzYW1sMjpFbmNyeXB0ZWRBc3NlcnRpb24geG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjx4ZW5jOkVuY3J5cHRlZERhdGEgeG1sbnM6eGVuYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjIiBJZD0iXzk3ZjdiNjE5MjQxYWI4MzA5ZWU2OTY0Njc3NjM5MjIyIiBUeXBlPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNFbGVtZW50Ij48eGVuYzpFbmNyeXB0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjYWVzMjU2LWNiYyIgeG1sbnM6eGVuYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjIi8+PGRzOktleUluZm8geG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxkczpSZXRyaWV2YWxNZXRob2QgVHlwZT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjRW5jcnlwdGVkS2V5IiBVUkk9IiNfZWIzYmM2ODdmOTdiZTAwNTBjMWY5NDQwNTVmNDlkZTciLz48L2RzOktleUluZm8+PHhlbmM6Q2lwaGVyRGF0YSB4bWxuczp4ZW5jPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyMiPjx4ZW5jOkNpcGhlclZhbHVlPlppT2w0ZXFlRWMrMEd3VFYxbmVCYnNoRlh2b0w5RTFqTWRSREk4anBaVzYzYjNSOFVrdVdvV2t3OGhEVWFkNE01dWJ0L0JMSWpQdmJHcGdVTzdsalMxUHgwS1E0MU9PU3pxQUpwS0Q3YUdTcGRSb3lCMklUSUNYSHdNUlJvK1cyRFZDQTNtTTE2bnFSbzN3Z25IQU82NG94QktXQXZHV0w1aVQ0a09FMUkzdk9xZGVNS3NnRmt5ZTAxZ0p0Q3RPVTlDdzdjL2RtMjM4emJZTnBRWHcxV3RVVHJtUVdBN2U4TW9vTTViNUFxOGxWZzRpelUvSE10cHA2UVpwTEhuR1pUYUVxYjVmL21uemt3bTJCOXkzejVpdWpSSlp1U2VRME1CVndwTUw0MXJabGRxaE9yT3dYMlA4d3dJeEtjdmo5WWdLUzJ4SXVNSktmb1M3T0o0Sy82SHVmWnJodkk2VkczQVFNMTdlVXpNU3dIUmJYWXZ3RXRRaDlJVTRZT3Mya1A3REpiakJFbWJBTHJIbnBMeStyem4yUlpBdm1mY1ZTbnN0SGJYQm1mQUE2L3RWS0FiVDFQM3dJc0pzY0kwQzMycGI2ZTZ1MHZTNzhld0dyN3JoaXBtR3JMMmYrQng3djhGcmhraE9TVitlYzNtVERyeVNTVU5XU3RkRWVGVnJxSDc3ck44RUFnVDErL21uUUdnTmh4a055ajhrend4QzUybGdlSHk3bG1RQlg2SmplVlRHS29qZGhQS1A5OE41MnhZT2Z5WjlTQXRFU1RhSVRYL0JuRXhGK1hoNHpzMDZmUGVUM3hNc2dTSlNPUlRjb3VEQkhXbXFUeGh5Ny9FdVZkMzNNMXA2MmRlVXNhSFRUaTNPcWt5amZzZVAzdzJVV2lnWjdRR1A0QkVEZ3FwWkg5R3Z4Tk1tZGtQWjRYcmg3cHhIaXdheHdlb2daYXRndys1Y0grSkN5SXNiWGZlWlFZUERSR1R4OWdkMHBJNTM0YVhoNFFXVEt0c2YrWmZOVXp6SmY4ZXVTV1JIdEVlOXBkaEZaMm5aN0x1MkJmWncxRkpZSHFZSzQwS2ZMWWNQOHJGZnRRVDhqK2FyQUFqVjY3YzZWYWdwTHBHU1JyY2xmcDg0d20xWWFxMGpweUV0ejdTQjNFZWJBZFV2blUvNEg5amhFUExONUNLNXJEdWVlSURMT3dHblB0RGtGVzFqclEvcGhoUURUWWJMSlgwVFhpeXpNMGxjL2ZNOVV6NWw3anoxOFltWHgyMzRRV2d1eUFSZUNhV29UeUFxVy9iSjMwRWRjTjRrZnhXOVByVFYreGlzaTB2SnczR24xeFZhdjA1Wmpyckd5T2RtVXJHNzdidmYvaWxEa1RSUmNidG5xRDRsYVNSTzA3bkFTd3FyVWNvckppRHdpbldFMjhiMkUxVXF1aGhiWDFrYkJPdmVPZEg4MXljNmtyeFlsU0FaS0puRVA1V2gyWEE3a0RpeVpJK3VFK0xibDI1OXFuWTNFQ1dpSE43clBUTWZ1dHpaTkpsOWJlMVVGNEo4WFplZlpBbU5JbEJsZVpBaUpNZHpHNm9FMTcxNkVscUFManJVN0IzQlhodlY4YVJqYlkvTG9JMlMzUUNkcGRmaXRNL24wLzhVYWV1RDYvSTZWcDhCTFBJaGx6WktYcGJsMjlSakltSkxpV3RCc1gvaWNkMVNCVm0rUUdIMFcvZFB4dkF0Q05VWm1ZeUh2RVNTaG8xb05LbFJXSWNoV3hEVEtZSkhEc3NOR3lhR2pVZ3NJUEVWd0dzWkI2U0VkK3RxbTN5TTFHYmtkdVQ3KzUvblU0Q2VHczZ4MnVCUCtqa1g2QW84Z1crbU1mUmQxR0hMNHBmdlhGSU9JUElCV28yOHR2TjlmeFN3Q3ZHYmhxZ0M3TGwyVnpKSkJwT1E1SzZjRXMwNVhPbHFMZ3FuS3F2NlEwUVFtU0tId2c5L3RNSFdpK1lWM09WNjlPSU9mVHRSeDRsVzJlenlSMnZsdzRaeFBVcUtXTnZYSU41aWVYdGoyQXdOUzAxOVNFUnZuTm9kRmJZMlNpRkNaNERaU2x5ZlFlRlZPWDFNL2VtY0RGV1p6SmNPOENTS3RPUjF2eERoNEZuTUVuazUvaWVpS1VZUkxkakpXUEFGWGo4K1lpZU5qUldnMnFkMVhMVmhwMUNMZjBCdUxTWGNYLzh6VyswdUcrbU5FNFBGWlFwYWdlcGFFMUUzSHlRS0pRdWNoSS9kSWtPQ21pWWtxRHpYVCt4T1RJcEgrUXpYVGJ5Mm5ITHpEdzBBRkNBcWlha2Q4M1ZVNnJMbDlZRnNBQlFuZFR3ckpRUTZQeDF6eHk1cjlYNDBpcVkxQ2cvRHlKZ0hUYXVtV3BEblo1SWZsM0tUSXQ5dk5VeTJaY20rRnVKV0F2MU15dmxpUXNZQ2V5S0tqMVgvQXFCZzBLeVRQbnNBRjFKc2NjaUR5dGovUjZmUUtySXp1eGYzVVFCdmxFN1cxSkY5UWZ3MHZ1Nm5WSDFPTkFjOUVZRGRubXVYSTViWGRHREVUcEY1dXNqdHZtWGtqL1FFcVJsM2VvRWxRVTVEYlU5SzVFU3VzbExaWVVVZ0E4ZHpIdDNWK3dMMTVjVitjWFpMMnBRQWhJM3YvQ0kvRDJkbjVQUEgzL2cxODV6Ylhyc1R4UHdEUkxXZkg1Z0FldnRseE5WSTRhVElqQzJKbml0cW82VkhxNVV0TG51NHpJR0JFWWZzYUo0eE9oVDczdWF1MmVtRmtNdlhyazh5dTNBeEphMEpLNWZzanNsTW5QN2U3dHpCS1Bobmd2ekxYWkR0dGxZLzlxdFdFNGRTTVFCNmx2ZjN0K2RmTDFHNFlvUDdrdDVoeExNTjFFQS9DZkhhaUJOU1Awa1YyWmJJb3luQkg2SzVNRzBjZTVlL2RNVTBXOWhuVkVreGQvKyt4emk3SWwvNHlVc04xcjVVbFRKeXlqNFZPSVM2TEcvRE02dW02N0R4Y0psL1B2a3RxUjk2TFhWak50c0Z1QTFMUU81YTU2SXlyTWlqL1pzU2ZlR1VaSjJ1SVhIdmNVQmw2L0ErUWV5VFZTNVNFTW0rQ1YvY2l5a2poTGtlRVgxUE1jcE51N3Yya3VZWm9yNTNRU2VPNEltV3F6S3N4UjNuZ0lLb0VWcGEvbmxCK2dNMStPVkkrOVYvU2U2SkRLVS9WMitUSnRIZy83WEFjWisvM2M3VmFvVTkvakNjZzBXMTVzOGVXemtMeFo1RkFVNzZ0RmRqT3RQUGlSMUdReDJmZmJxWENPYlFvRVBHeHh3ZVQ0NUNydllvcHV2Z3I5M1pyNmh6a1A1NEtSVE4wNHBrelQ4WEg2Unhsb2FkeDEvWUVRTVVpVVpFRlIrbUpsZmRtaWF1M1drcUVxNThWUzZ5Um5RaXQ0NmZYbkxpeE1sYVd2WC9BYUVYdDlRQjIwSkJlc1V1MDVVOE85SW9UQmphSElSQ1lreXdKZkpQd0Jmay8zbHp4VHNrNzRvWGRlMjdnZ20zSVYvNENkQTh6R1QvR3hWRUdwWHZHNlZDRnV1cjA1MlVoVVRMSmtxQW92WVdIa3FmMGE3YzdxK0V6YkRYY29WaWNDQUZ6K0Z3RmNIQTFkdVNwanRRc1I0OWUzN2c5Z0MzY1lrcHBCQlZ1ZkhZTXVTdjBuQ1lRMkJJajI4Y2czcWdVWDhGS0puek5Tb29kMHdiU29aVytTOExsVDZ2Sm9zbElvVnNqQk9DSDlETEVsTXdLbmRwVDlpSUVNeFdoQ1BqaE1rWWlLREFSa1BGVVA1eTE5UTY3UW5jM0ZYeXR3ZU9abi9ZZW96ZzFIOGQzenNtM3d0UWJDcmdGZ25yK3cxL3dUeVlPeVU2eW1JdGt2eU9DOWNPT0p3aHBPN1FKR1l6d1FuMkRVaWlCUFJHOGs4YzNpZ3h6bXFOeDlzeXRDakE1TnMxMHd0RCtCUFRjUmhidmpkWFdEbmorSTdhYUtCd1VOMGkyZnVaelEwWE9ZZzhlK2VDSHMvMXlhQk9MdGFJVmJpWmdKeW9RUWU1MUZHMWlNSmx0NXRGMUloeFplRGR0amNSbzFBMzBvTjRCdHFXK083QzJsc0ZCNThTYkJ2NmVQVWl1UGpFL1BxMEhwWktGaml5dzBaT2RMWFZTM2R1RXJSU1JHaXQ1b3JzUzcwNDAxR1J5Mi80a0ZjcWdNcVFkSDZMOTBjZTBPMy9SZ0JIS214UGRET25hK2VEVWJhUytnZnFBZWVPOGpQanhPMTRrS2xTUE51eGVDSjBSdGtncVQzcXA5eUtubmdpWGJQYnlqbUxncGgvTmZzYWhUMEVrY3pFcHJyclZoYjBtanlHYlZldGpyamhLSWk5VHczSDBPeTliMHh5UjZFejZrenpVR1d5NzI0cUsvek9taVdDd2tjV2lPbHp1S3pTUmt4dGZ4WGxjK2V0V1BYM0Nld0hSUytKcHNHdmRaZG55Q0xneWMrWVBYeG92OGNNeTZGRDZTQ2NERHExLzlQVDV5TjhFQ1p2TFZtNFdzR2FLaHdqbUZOdmRVd2VIZnNPVFlocTNFTjlTUHVrSThsVkc4SGo2bHhqM2lRMUhRMFNqUHp6bjBoZTczYzlNM1RhTFltRmpneWhvdGNFNE00REZiVFBZTHFyVFBFTFo1VkZPNWVWcUhQdGN0aDNWcDhqNG9VU3VnZkg4YldZQ011TGlqT1pWUGl1OWRTcDI1RHIrQ2svWERVTTdIS2kzT1FlR3FnVUc3T1lOMlJTS3lTVlR0SndSYVVtRGp0OW9GSUVTdzF1RDBpc0VQRDR4eE1WMXdlYmxhSVhPenhxOGRSS0thVm9INEtlbjhlOFQvUW9WR1BEbldybmh3ZTN3RWg5MmV3VHk5ek1ORzJ3YjRaZnd1QmMzMUNlVENreTVmTDNzd2NhVjNlTzZidCswQS9tTXNPYXdjRE1SRkxZQjhkdHorSlNTL2NZTzE5UFpyNXJUVzlJTFJrTFpCSmcvN3pDYm02WWdEdDJ0YlhheUF1azM3cFZyQ3NaZkRENzRWOTgzR0Nac3ZMVC9jU0dUaWFyay9FaTJxS3ZnWW05aUE4Mkg3cUtkbWNUcXJObGVJeG9Da2d3NWJlNENzTWkvK0VDS1BIUUZTZDloY0hMUmxneTlvMmliOHBMbGY2bzlUcHlwMUFlaHBCWnZmRUV5WHhXM282cWhZS1h0c2VoTXZ0TTFiamJXaEhXODc3bVg1cDNQY1NhKzZNT2hIdmpPRnpUOUg3YkRKTEN4RUxZYU0xYnBxOWNualFiaDdUWVc0MDRUS3JNRmhLbk45bStlZ2oxNzV4MVBYVm8wdkFGTFBhNEJDT3BKR2h3bWF3Uko2cks0OTdkYVl0QVd0TDdWZ0ZwS1VPQm1BU0lDZXNSdHo1dmpJclJyM2RRNk1IRkk5OXpVcWRGK24vUDNWZkE1UHNxbnJ6VnRjZDQzRlZ0S0drZzZ5ejlyYmRCRm84M3JuanIwMFFQYkxVQmxDcTdlWnB4aU93anZUZnhjTU11OEgwLzJ5N2dYV1V3d01lcEtQeGVRdlBGMWpxS3NPR2tXY3pnMFhqYXpPYWlxdkNjUEdNMHQ5SHpHeGlZaVNrRmpxMkF0MFNJbGFLZElyejZtdXloNnRjNE1HUnZwQlhNZlZHRkNUZXU5ODZ4R2dKYy9YSjl4Z3BHbkJmM2JIQXJnWllwaTJjemVsSmg0M1pMWDRjSERnNTdUT0RFWFFZR2hGTTFPTm51N0hMVEJGdkVEKzNmVllPdDAvQSszQmRubFZsNmN4dkFiZnJDdmtoZ3h2ZGdxOE5CT0VsMUxhVzF3QjJwcW5xM3Q2dE1PQ2FkUnVkKy9CVEh0S0QyWlFtdk1LZ01BMzJIS0JleXFNallvZU8xcTdxa3paQmpYOENKNW9nZjBNS0xYK0kveDJkckozVFluVDFXUVd2SEVhbjJ5ZXZHaDk1TXVTdzd6K2NYb1FrL09oY0ZSdFZaeVZKNThwV0lvdjM2YjJ6QkNBbFdlcFQ0RnpjanI4Z1U2NXBDdzhYZWxNekM0RE04cmNPZW16L1JUcFV4cUxoYzJ1dnR2OWJIclBIWFNQUjVlajAzUndBSlBDNE9CWkZaRHI3TVdXVkY0dDI4ckhsOGFEN3NsSmxrcFZjU2p1aVhvak8zNWZHUjgvSFdyelhCSHkxTEgvVDFwMHhkRW9HaE9Db3NJaDBaSjkrMmFWOG0ySStIeXg0SVRXM2E1VXNqc3pvOGE2VzRMVmk3R3FydXNud043NjZjSmFXTmZoSkFIeUQyZCtWNVlRU1llNk96NHVwL1RGNjU1UWY5ODd2SjZwbGxkZ2U5UmY3dVRlNVhXZnhLWHUyN2IzNW5ZMTlYcDdLOE9iT0JlYThqWGRZbG14ZEZ3VHdvbWJJY1ZseER5V1lSbmpiUzlTSjd1RFNOenRYdUNFRW1WOUFERTNnNWI3R21PbTZoRjlpcWJ2bCtzeDZ1MElPMzhzbEc1S2N6RWJBek5icEZWMkV2blh4Q2s5dVg5aFpmMkg0WHdFa25XcWNHQlVHS1dTQUlSeGc4Wkl5ZTAvKzJPWS9BRzB6aGhEK1BTNDIrY2l6V2ZPd0JmeFNwSUx4a01hM3pwMWc4ajRTYWg2OFZEYVB6U1BMb2Y0S0hCZmtXZU1VNW5GbWJxTVFLaHRtaWVwa1J5K0srLzNOVmNJWURWK0RZblRnTnA5cGljbnE3eUZYQXdhUk9uYzJDTHJLUURhSkFPcG80cTRKKzlTZVZXM1B5MnJMWTRreTdvbk44OTFNMmtwVVNlMlFCVWRxVmFDeFBvN0UreGc4L2k0WS9YalZkcnQ5T2JqMGdyUExZbzdyNFZWVUFtRUhsbENKejhsTlJzQU1CR1ZIdnZyRlZ0VEhVc0VQRTM0THA3QjhRaDc0dThFY2hJMkFCdDZic01weEVSbytBeFQxc3RIT0M3SFBHMUtHanJLU1V0RWlkaEUwZ3lGb1lmUWh5d0U3V1p1b1dZUWRJOFJtV3YyWXpRNUFFSXVFQmRjVnJYWVhkRlB1aklOdmZuT2JjV0hRYWRiWHV5dUVySjhKU1lyMm5zQ2lGeVp0SHQwaVpvT1h1R0JpbDh5WkdxQVp0SVNpZ1hDSTBMai9qbnFqLzRUcTJ6ODgwRko2UFhwZ1lZWlE9PTwveGVuYzpDaXBoZXJWYWx1ZT48L3hlbmM6Q2lwaGVyRGF0YT48L3hlbmM6RW5jcnlwdGVkRGF0YT48eGVuYzpFbmNyeXB0ZWRLZXkgeG1sbnM6eGVuYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjIiBJZD0iX2ViM2JjNjg3Zjk3YmUwMDUwYzFmOTQ0MDU1ZjQ5ZGU3Ij48eGVuYzpFbmNyeXB0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjcnNhLTFfNSIgeG1sbnM6eGVuYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjIi8+PGRzOktleUluZm8geG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUNRakNDQWF1Z0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRMEZBREErTVFzd0NRWURWUVFHRXdKMWN6RUxNQWtHQTFVRUNBd0MNClEwRXhEakFNQmdOVkJBb01CVXRaVms5VE1SSXdFQVlEVlFRRERBbExXVlpQVTFSRlUxUXdIaGNOTVRZd09UTXdNRGt4TlRFMVdoY04NCk1UY3dPVE13TURreE5URTFXakErTVFzd0NRWURWUVFHRXdKMWN6RUxNQWtHQTFVRUNBd0NRMEV4RGpBTUJnTlZCQW9NQlV0WlZrOVQNCk1SSXdFQVlEVlFRRERBbExXVlpQVTFSRlUxUXdnWjh3RFFZSktvWklodmNOQVFFQkJRQURnWTBBTUlHSkFvR0JBTWdId3cwckZsaUgNCnYvOHlBZmVXVGd6M09JRGJZR2oxM0FXS3hoS0hIRWxtelZpWVlqNkFjR1g3aUY0MDZSVkFGWURQYWJxdVkwQXhvcHZ4Nm9TUXplbHQNCjdlb2xScDhwckNuSnBRdzBHOTlCZXQ2a2UvcXJSQzRrajdGaWVFT0Y4UFRWanU5cWZzSURKMTJpTFlPejZWMkwvNUtpWGoxTktBZVUNCmlwZVU3cE1qQWdNQkFBR2pVREJPTUIwR0ExVWREZ1FXQkJUVjlvTGdqWXk5STdubngzQWdkRVI0SFhKN2JqQWZCZ05WSFNNRUdEQVcNCmdCVFY5b0xnall5OUk3bm54M0FnZEVSNEhYSjdiakFNQmdOVkhSTUVCVEFEQVFIL01BMEdDU3FHU0liM0RRRUJEUVVBQTRHQkFJcUcNCkdxem5KeFV0VnZyVkxpZ0JPV1JTOFplSWdJT29UUHlTLzgrTi9lUk5Fc2dnL2ZWaW5mSGRkUlJsOWFPM3gxeHhlWXF5ZEpYL0xNN0MNCjhDY1kvWEZNQkpLM0JOQlY5K1JJeHNGd1NydUdJdDRabGo3bWppTmwwQUFVQ25JOXlDRFgzWU1WUWtnb2JFaGdPRktWTnZGSjhOMkwNCkpZaW5mVjhWNTRucWg4L0g8L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48eGVuYzpDaXBoZXJEYXRhIHhtbG5zOnhlbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyI+PHhlbmM6Q2lwaGVyVmFsdWU+a25zVGFhY1hVbjB0U1hxNVRYZEd1Vm1nUG1vaFlJZEdpSGpwREFXeC91dzJld1RUM0N4eksyV3NEYzlUcmk2aUFQSjN4MjIrQm1hTy9iM0pLU25XNTM0WHVHSnpCaWpCN24ydThlRmVOa2ZldkJ3VUszWGxJV0p5bjY1OGJrRXpDRGRDa2lvRWVEZy9MQUNqd0g0VGVDaXlQSEN6MUUwSkhEMGVLbUw4c09nPTwveGVuYzpDaXBoZXJWYWx1ZT48L3hlbmM6Q2lwaGVyRGF0YT48eGVuYzpSZWZlcmVuY2VMaXN0Pjx4ZW5jOkRhdGFSZWZlcmVuY2UgVVJJPSIjXzk3ZjdiNjE5MjQxYWI4MzA5ZWU2OTY0Njc3NjM5MjIyIi8+PC94ZW5jOlJlZmVyZW5jZUxpc3Q+PC94ZW5jOkVuY3J5cHRlZEtleT48L3NhbWwyOkVuY3J5cHRlZEFzc2VydGlvbj48L3NhbWwycDpSZXNwb25zZT4=

0 commit comments

Comments
 (0)