diff options
| author | Thomas Lenz <thomas.lenz@egiz.gv.at> | 2020-10-13 18:32:49 +0200 | 
|---|---|---|
| committer | Thomas Lenz <thomas.lenz@egiz.gv.at> | 2020-10-13 18:32:49 +0200 | 
| commit | e6f5094b2db9d85a28009c288a6c923e336c82b4 (patch) | |
| tree | 2ec1f3c357c3b576915b8a202fe4515734439317 /eaaf_modules/eaaf_module_auth_sl20 | |
| parent | 54fdf1c8201312ffedec71595aa6602bb11e7640 (diff) | |
| parent | 03bde4a2dda7880007f9910ffecddc0ca8a4b7ba (diff) | |
| download | EAAF-Components-e6f5094b2db9d85a28009c288a6c923e336c82b4.tar.gz EAAF-Components-e6f5094b2db9d85a28009c288a6c923e336c82b4.tar.bz2 EAAF-Components-e6f5094b2db9d85a28009c288a6c923e336c82b4.zip | |
Merge branch 'nightlyBuild' of gitlab.iaik.tugraz.at:egiz/eaaf_components into nightlyBuild
Diffstat (limited to 'eaaf_modules/eaaf_module_auth_sl20')
7 files changed, 169 insertions, 41 deletions
| diff --git a/eaaf_modules/eaaf_module_auth_sl20/pom.xml b/eaaf_modules/eaaf_module_auth_sl20/pom.xml index b40e8add..e67f0dc0 100644 --- a/eaaf_modules/eaaf_module_auth_sl20/pom.xml +++ b/eaaf_modules/eaaf_module_auth_sl20/pom.xml @@ -79,6 +79,16 @@        <artifactId>provider</artifactId>        <scope>test</scope>      </dependency>  +    <dependency> +      <groupId>iaik.prod</groupId> +      <artifactId>iaik_jce_full</artifactId> +      <scope>test</scope> +    </dependency> +    <dependency> +      <groupId>iaik.prod</groupId> +      <artifactId>iaik_eccelerate</artifactId> +      <scope>test</scope> +    </dependency>    </dependencies> diff --git a/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/JoseUtils.java b/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/JoseUtils.java index d8c39931..48b10580 100644 --- a/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/JoseUtils.java +++ b/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/JoseUtils.java @@ -2,12 +2,19 @@ package at.gv.egiz.eaaf.modules.auth.sl20.utils;  import java.io.IOException;  import java.security.Key; +import java.security.KeyFactory;  import java.security.KeyStore; +import java.security.NoSuchAlgorithmException;  import java.security.Provider; +import java.security.PublicKey;  import java.security.cert.CertificateEncodingException;  import java.security.cert.X509Certificate;  import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey;  import java.security.interfaces.RSAPrivateKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec;  import java.util.Collections;  import java.util.List;  import java.util.Map; @@ -15,13 +22,8 @@ import java.util.Map.Entry;  import javax.annotation.Nonnull; -import at.gv.egiz.eaaf.core.exception.EaafKeyUsageException; -import at.gv.egiz.eaaf.core.exceptions.EaafException; -import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreUtils; -import at.gv.egiz.eaaf.core.impl.data.Pair; -import at.gv.egiz.eaaf.core.impl.utils.X509Utils; -  import org.apache.commons.lang3.StringUtils; +import org.bouncycastle.jce.provider.BouncyCastleProvider;  import org.jose4j.jca.ProviderContext;  import org.jose4j.jwa.AlgorithmConstraints;  import org.jose4j.jws.AlgorithmIdentifiers; @@ -32,6 +34,11 @@ import org.jose4j.keys.resolvers.X509VerificationKeyResolver;  import org.jose4j.lang.JoseException;  import org.springframework.util.Base64Utils; +import at.gv.egiz.eaaf.core.exception.EaafKeyUsageException; +import at.gv.egiz.eaaf.core.exceptions.EaafException; +import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreUtils; +import at.gv.egiz.eaaf.core.impl.data.Pair; +import at.gv.egiz.eaaf.core.impl.utils.X509Utils;  import lombok.AllArgsConstructor;  import lombok.Getter;  import lombok.extern.slf4j.Slf4j; @@ -45,6 +52,8 @@ import lombok.extern.slf4j.Slf4j;  @Slf4j  public class JoseUtils { +  private static final Provider provider = new BouncyCastleProvider(); +      /**     * Create a JWS signature.     * @@ -161,7 +170,10 @@ public class JoseUtils {      // set signing information      final Pair<Key, X509Certificate[]> signingCred = EaafKeyStoreUtils.getPrivateKeyAndCertificates(          keyStore.getFirst(), keyAlias, keyPassword, true, friendlyNameForLogging); -    jws.setKey(signingCred.getFirst()); +     +    // set verification key +    jws.setKey(convertToBcKeyIfRequired(signingCred.getFirst())); +                    jws.setAlgorithmHeaderValue(getKeyOperationAlgorithmFromCredential(          jws.getKey(), rsaAlgToUse, eccAlgToUse, friendlyNameForLogging)); @@ -173,7 +185,7 @@ public class JoseUtils {            keyStore.getSecond().getName());        jws.setProviderContext(providerCtx); -    } +    }       if (addFullCertChain) {        jws.setCertificateChainHeaderValue(signingCred.getSecond()); @@ -216,6 +228,8 @@ public class JoseUtils {        log.trace("Sorting received X509 certificates ... ");        final List<X509Certificate> sortedX5cCerts = X509Utils.sortCertificates(x5cCerts); +       +              if (trustedCerts.contains(sortedX5cCerts.get(0))) {          selectedKey = sortedX5cCerts.get(0).getPublicKey(); @@ -247,10 +261,10 @@ public class JoseUtils {        throw new JoseException("Can NOT select verification key for JWS. Signature verification FAILED");      } - +          // set verification key -    jws.setKey(selectedKey); - +    jws.setKey(convertToBcKeyIfRequired(selectedKey)); +           // load payLoad      return new JwsResult(          jws.verifySignature(), @@ -260,6 +274,48 @@ public class JoseUtils {    } +   +  /** +   * Convert an ECC public-key into BouncyCastle implementation. +   *   +   *  <p> IAIK JCE / Eccelerate ECC Keys are not compatible to JWS impl.</p> +   * @param input Key +   * @return input Key, or BC ECC-Key in case of a ECC Key  +   */ +  public static Key convertToBcKeyIfRequired(Key input) { +    try { +      if (input instanceof ECPublicKey  +          && "iaik.security.ec.common.ECPublicKey".equals(input.getClass().getName())) { + +        //convert Key to BouncyCastle KeyImplemenation because there is an  +        //incompatibility with IAIK EC Keys and JWS signature-verfification implementation +        PublicKey publicKey = KeyFactory.getInstance( +            input.getAlgorithm(), provider).generatePublic( +                new X509EncodedKeySpec(input.getEncoded())); +        return publicKey; +           +      } else if (input instanceof ECPrivateKey  +          && "iaik.security.ec.common.ECPrivateKey".equals(input.getClass().getName())) { +        //convert Key to BouncyCastle KeyImplemenation because there is an  +        //incompatibility with IAIK EC Keys and JWS signature-creation implementation +        Key privateKey = KeyFactory.getInstance( +            input.getAlgorithm(), provider).generatePrivate( +                new PKCS8EncodedKeySpec(input.getEncoded())); +         +        return privateKey; +       +      }  +             +    } catch (InvalidKeySpecException | NoSuchAlgorithmException e) { +      log.warn("Can NOT convert {} to {}. The verification may FAIL.",  +          input.getClass().getName(), PublicKey.class.getName(), e); +       +    } +     +    return input; +     +  } +      /**     * Select signature algorithm for a given credential.     * diff --git a/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/JsonSecurityUtils.java b/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/JsonSecurityUtils.java index 10cfeafa..27f06276 100644 --- a/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/JsonSecurityUtils.java +++ b/eaaf_modules/eaaf_module_auth_sl20/src/main/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/JsonSecurityUtils.java @@ -13,24 +13,6 @@ import java.util.List;  import javax.annotation.Nonnull;  import javax.annotation.PostConstruct; -import at.gv.egiz.eaaf.core.api.idp.IConfiguration; -import at.gv.egiz.eaaf.core.exception.EaafKeyAccessException; -import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; -import at.gv.egiz.eaaf.core.exceptions.EaafException; -import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreFactory; -import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreUtils; -import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration; -import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration.KeyStoreType; -import at.gv.egiz.eaaf.core.impl.data.Pair; -import at.gv.egiz.eaaf.core.impl.utils.X509Utils; -import at.gv.egiz.eaaf.modules.auth.sl20.Constants; -import at.gv.egiz.eaaf.modules.auth.sl20.data.VerificationResult; -import at.gv.egiz.eaaf.modules.auth.sl20.exceptions.SL20Exception; -import at.gv.egiz.eaaf.modules.auth.sl20.exceptions.SL20SecurityException; -import at.gv.egiz.eaaf.modules.auth.sl20.exceptions.SlCommandoBuildException; -import at.gv.egiz.eaaf.modules.auth.sl20.exceptions.SlCommandoParserException; -import at.gv.egiz.eaaf.modules.auth.sl20.utils.JoseUtils.JwsResult; -  import org.apache.commons.lang3.StringUtils;  import org.jose4j.jca.ProviderContext;  import org.jose4j.jwa.AlgorithmConstraints; @@ -50,6 +32,24 @@ import org.springframework.util.Base64Utils;  import com.fasterxml.jackson.core.JsonParseException;  import com.fasterxml.jackson.databind.JsonNode; +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.exception.EaafKeyAccessException; +import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; +import at.gv.egiz.eaaf.core.exceptions.EaafException; +import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreFactory; +import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreUtils; +import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration; +import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration.KeyStoreType; +import at.gv.egiz.eaaf.core.impl.data.Pair; +import at.gv.egiz.eaaf.core.impl.utils.X509Utils; +import at.gv.egiz.eaaf.modules.auth.sl20.Constants; +import at.gv.egiz.eaaf.modules.auth.sl20.data.VerificationResult; +import at.gv.egiz.eaaf.modules.auth.sl20.exceptions.SL20Exception; +import at.gv.egiz.eaaf.modules.auth.sl20.exceptions.SL20SecurityException; +import at.gv.egiz.eaaf.modules.auth.sl20.exceptions.SlCommandoBuildException; +import at.gv.egiz.eaaf.modules.auth.sl20.exceptions.SlCommandoParserException; +import at.gv.egiz.eaaf.modules.auth.sl20.utils.JoseUtils.JwsResult; +  @Service  public class JsonSecurityUtils implements IJoseTools {    private static final Logger log = LoggerFactory.getLogger(JsonSecurityUtils.class); @@ -269,7 +269,7 @@ public class JsonSecurityUtils implements IJoseTools {        }        // set key -      receiverJwe.setKey(encryptionCred.getFirst()); +      receiverJwe.setKey(JoseUtils.convertToBcKeyIfRequired(encryptionCred.getFirst()));        // decrypt payload        return mapper.getMapper().readTree(receiverJwe.getPlaintextString()); diff --git a/eaaf_modules/eaaf_module_auth_sl20/src/test/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/AbstractJsonSecurityUtilsTest.java b/eaaf_modules/eaaf_module_auth_sl20/src/test/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/AbstractJsonSecurityUtilsTest.java index 917ef1e0..8516a0ed 100644 --- a/eaaf_modules/eaaf_module_auth_sl20/src/test/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/AbstractJsonSecurityUtilsTest.java +++ b/eaaf_modules/eaaf_module_auth_sl20/src/test/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/AbstractJsonSecurityUtilsTest.java @@ -9,15 +9,6 @@ import java.security.Security;  import java.security.cert.CertificateEncodingException;  import java.security.cert.X509Certificate; -import at.gv.egiz.eaaf.core.exceptions.EaafException; -import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreFactory; -import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreUtils; -import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration; -import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration.KeyStoreType; -import at.gv.egiz.eaaf.core.impl.data.Pair; -import at.gv.egiz.eaaf.core.test.dummy.DummyAuthConfigMap; -import at.gv.egiz.eaaf.modules.auth.sl20.data.VerificationResult; -  import org.apache.commons.lang3.RandomStringUtils;  import org.bouncycastle.jce.provider.BouncyCastleProvider;  import org.jose4j.base64url.Base64Url; @@ -38,6 +29,15 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;  import com.fasterxml.jackson.databind.JsonNode; +import at.gv.egiz.eaaf.core.exceptions.EaafException; +import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreFactory; +import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreUtils; +import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration; +import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration.KeyStoreType; +import at.gv.egiz.eaaf.core.impl.data.Pair; +import at.gv.egiz.eaaf.core.test.dummy.DummyAuthConfigMap; +import at.gv.egiz.eaaf.modules.auth.sl20.data.VerificationResult; +  @RunWith(SpringJUnit4ClassRunner.class)  @ContextConfiguration("/spring/test_eaaf_sl20_hsm.beans.xml")  public abstract class AbstractJsonSecurityUtilsTest { @@ -78,7 +78,7 @@ public abstract class AbstractJsonSecurityUtilsTest {      final JsonWebEncryption jwe = new JsonWebEncryption();      jwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.ECDH_ES_A256KW);      jwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_128_GCM); -    jwe.setKey(joseTools.getEncryptionCertificate().getPublicKey()); +    jwe.setKey(JoseUtils.convertToBcKeyIfRequired(joseTools.getEncryptionCertificate().getPublicKey()));      jwe.setX509CertSha256ThumbprintHeaderValue(joseTools.getEncryptionCertificate());      jwe.setPayload(payLoad); @@ -141,7 +141,7 @@ public abstract class AbstractJsonSecurityUtilsTest {      final JsonWebEncryption jwe = new JsonWebEncryption();      jwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.ECDH_ES_A256KW);      jwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_128_GCM); -    jwe.setKey(key.getSecond()[0].getPublicKey()); +    jwe.setKey(JoseUtils.convertToBcKeyIfRequired(key.getSecond()[0].getPublicKey()));      jwe.setPayload(payLoad);      // set special provider if required diff --git a/eaaf_modules/eaaf_module_auth_sl20/src/test/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/JoseUtilsTest.java b/eaaf_modules/eaaf_module_auth_sl20/src/test/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/JoseUtilsTest.java new file mode 100644 index 00000000..7771ce60 --- /dev/null +++ b/eaaf_modules/eaaf_module_auth_sl20/src/test/java/at/gv/egiz/eaaf/modules/auth/sl20/utils/JoseUtilsTest.java @@ -0,0 +1,58 @@ +package at.gv.egiz.eaaf.modules.auth.sl20.utils; + +import java.io.IOException; +import java.security.NoSuchProviderException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.io.IOUtils; +import org.jose4j.jwa.AlgorithmConstraints; +import org.jose4j.jwa.AlgorithmConstraints.ConstraintType; +import org.jose4j.jws.AlgorithmIdentifiers; +import org.jose4j.lang.JoseException; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.BlockJUnit4ClassRunner; + +import at.gv.egiz.eaaf.modules.auth.sl20.utils.JoseUtils.JwsResult; + +@RunWith(BlockJUnit4ClassRunner.class) +public class JoseUtilsTest { + +  private static final List<String> BINDING_AUTH_ALGORITHM_WHITELIST_SIGNING = Collections.unmodifiableList( +      Arrays.asList( +          AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256, +          AlgorithmIdentifiers.ECDSA_USING_P521_CURVE_AND_SHA512, +          AlgorithmIdentifiers.RSA_PSS_USING_SHA256, +          AlgorithmIdentifiers.RSA_PSS_USING_SHA512)); + +  @Test +  public void testBindingAuthBlock() throws JoseException, IOException, CertificateException, NoSuchProviderException { +     +    final String serializedContent = IOUtils.toString(JoseUtils.class.getResourceAsStream( +        "/data/bindingAuth1.jws"), "UTF-8"); +         +    final iaik.x509.X509Certificate trustedCert = new iaik.x509.X509Certificate(JoseUtils.class +        .getResourceAsStream("/data/bindingAuth1.crt")); +             +    final List<X509Certificate> trustedCerts = Arrays.asList(trustedCert); +    final AlgorithmConstraints constraints = new AlgorithmConstraints(ConstraintType.PERMIT, +        BINDING_AUTH_ALGORITHM_WHITELIST_SIGNING +            .toArray(new String[BINDING_AUTH_ALGORITHM_WHITELIST_SIGNING.size()])); + +    final JwsResult result = JoseUtils.validateSignature(serializedContent, trustedCerts, constraints); + +    Assert.assertNotNull("JWS verify result", result); +    Assert.assertTrue("JWS not valid", result.isValid()); +    Assert.assertNotNull("JWS payload", result.getPayLoad()); +    Assert.assertNotNull("JWS Headers", result.getFullJoseHeader()); +    Assert.assertNotNull("JWS Signercerts", result.getX5cCerts()); +    Assert.assertEquals("Signercerts size", 1, result.getX5cCerts().size()); +    Assert.assertArrayEquals("Signercerts", trustedCert.getEncoded(), result.getX5cCerts().get(0).getEncoded()); + +  } +} diff --git a/eaaf_modules/eaaf_module_auth_sl20/src/test/resources/data/bindingAuth1.crt b/eaaf_modules/eaaf_module_auth_sl20/src/test/resources/data/bindingAuth1.crt new file mode 100644 index 00000000..11c17e71 --- /dev/null +++ b/eaaf_modules/eaaf_module_auth_sl20/src/test/resources/data/bindingAuth1.crt @@ -0,0 +1,3 @@ +-----BEGIN CERTIFICATE----- +MIIBXzCCAQWgAwIBAgIIPuBGtvo16nUwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAwwPRHVtbXlQa2lTZXJ2aWNlMB4XDTIwMTAwNzEyMTAyMVoXDTIxMTAwNzEyMTAyMVowUTEpMCcGA1UEAwwgNWMzM2Q3MjdlY2YzZTAyYTE2NmYzYWI2NWZiYTEzOGExFDASBgNVBAoMC0VJRC1ERVYtUEtJMQ4wDAYDVQQLDAVULUVudjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABACA6RBPYIX3i0+TqYq2gb3XAD0B1/tee3/lP8sPc+tt6GFDN0Vsos77VojhRQnGRndmoWi9OW7KS5uQe+5++W8wCgYIKoZIzj0EAwIDSAAwRQIhAO7NlM4YfnapZ9Vam/LF/5ASPGbN4SK0fK4bhGHQw8yIAiB77JHkZIaDtgCcv7CSPf/mvldSf5ViPelhuZBPSLRUsQ== +-----END CERTIFICATE----- diff --git a/eaaf_modules/eaaf_module_auth_sl20/src/test/resources/data/bindingAuth1.jws b/eaaf_modules/eaaf_module_auth_sl20/src/test/resources/data/bindingAuth1.jws new file mode 100644 index 00000000..6ba84d97 --- /dev/null +++ b/eaaf_modules/eaaf_module_auth_sl20/src/test/resources/data/bindingAuth1.jws @@ -0,0 +1 @@ +eyJ4NWMiOlsiTUlJQlh6Q0NBUVdnQXdJQkFnSUlQdUJHdHZvMTZuVXdDZ1lJS29aSXpqMEVBd0l3R2pFWU1CWUdBMVVFQXd3UFJIVnRiWGxRYTJsVFpYSjJhV05sTUI0WERUSXdNVEF3TnpFeU1UQXlNVm9YRFRJeE1UQXdOekV5TVRBeU1Wb3dVVEVwTUNjR0ExVUVBd3dnTldNek0yUTNNamRsWTJZelpUQXlZVEUyTm1ZellXSTJOV1ppWVRFek9HRXhGREFTQmdOVkJBb01DMFZKUkMxRVJWWXRVRXRKTVE0d0RBWURWUVFMREFWVUxVVnVkakJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCQUNBNlJCUFlJWDNpMCtUcVlxMmdiM1hBRDBCMVwvdGVlM1wvbFA4c1BjK3R0NkdGRE4wVnNvczc3Vm9qaFJRbkdSbmRtb1dpOU9XN0tTNXVRZSs1KytXOHdDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWhBTzdObE00WWZuYXBaOVZhbVwvTEZcLzVBU1BHYk40U0swZks0YmhHSFF3OHlJQWlCNzdKSGtaSWFEdGdDY3Y3Q1NQZlwvbXZsZFNmNVZpUGVsaHVaQlBTTFJVc1E9PSJdLCJ0eXAiOiJiaW5kaW5nQXV0aCIsImFsZyI6IkVTMjU2In0.MzIxZmVmYTQtODVkOC00YmE5LWE0MmUtYWY4MzM3YTEyNTA1.diiXXegwv3Gu6ezJRxf7F5BnRxNhTnBXJ0D5RX4OqDxs2QvfzSPA4mOkUed18_56aILMBLVL-XIMszNILfp7OA
\ No newline at end of file | 
