Example usage for java.security KeyStore getCertificate

List of usage examples for java.security KeyStore getCertificate

Introduction

In this page you can find the example usage for java.security KeyStore getCertificate.

Prototype

public final Certificate getCertificate(String alias) throws KeyStoreException 

Source Link

Document

Returns the certificate associated with the given alias.

Usage

From source file:org.apache.xml.security.test.signature.CreateSignatureTest.java

String doSignWithCert() throws Exception {
    KeyStore ks = KeyStore.getInstance("JKS");
    FileInputStream fis = null;//  w  w  w . j  av  a2 s.c om
    if (BASEDIR != null && !"".equals(BASEDIR)) {
        fis = new FileInputStream(BASEDIR + SEP + "data/test.jks");
    } else {
        fis = new FileInputStream("data/test.jks");
    }
    ks.load(fis, "changeit".toCharArray());
    PrivateKey privateKey = (PrivateKey) ks.getKey("mullan", "changeit".toCharArray());
    org.w3c.dom.Document doc = db.newDocument();
    X509Certificate signingCert = (X509Certificate) ks.getCertificate("mullan");
    doc.appendChild(doc.createComment(" Comment before "));
    Element root = doc.createElementNS("", "RootElement");

    doc.appendChild(root);
    root.appendChild(doc.createTextNode("Some simple text\n"));

    Element canonElem = XMLUtils.createElementInSignatureSpace(doc, Constants._TAG_CANONICALIZATIONMETHOD);
    canonElem.setAttributeNS(null, Constants._ATT_ALGORITHM, Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);

    SignatureAlgorithm signatureAlgorithm = new SignatureAlgorithm(doc, XMLSignature.ALGO_ID_SIGNATURE_DSA);
    XMLSignature sig = new XMLSignature(doc, null, signatureAlgorithm.getElement(), canonElem);

    root.appendChild(sig.getElement());
    doc.appendChild(doc.createComment(" Comment after "));
    Transforms transforms = new Transforms(doc);
    transforms.addTransform(Transforms.TRANSFORM_ENVELOPED_SIGNATURE);
    transforms.addTransform(Transforms.TRANSFORM_C14N_WITH_COMMENTS);
    sig.addDocument("", transforms, Constants.ALGO_ID_DIGEST_SHA1);

    sig.addKeyInfo(signingCert);
    sig.sign(privateKey);
    X509Certificate cert = sig.getKeyInfo().getX509Certificate();
    sig.checkSignatureValue(cert.getPublicKey());
    ByteArrayOutputStream bos = new ByteArrayOutputStream();

    XMLUtils.outputDOMc14nWithComments(doc, bos);
    return new String(bos.toByteArray());
}

From source file:org.guanxi.sp.engine.form.RegisterGuardFormController.java

/**
 * Creates an authenticated certificate chain for the specified X509 name
 *
 * @param x509DN X509 name to for which to create a certificate chain
 * @param keyType The type of the key, e.g. "RSA", "DSA"
 * @return Returns a CABean instance encapsulating certificate chain and key information
 * or null if an error occurred//  ww w. j  a v a2  s  . c  o m
 */
private CABean createSignedCertificateChain(String x509DN, String keyType) {
    try {
        // Create a public/private keypair...
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance(keyType);
        keyGen.initialize(1024, new SecureRandom());
        KeyPair keypair = keyGen.generateKeyPair();
        PrivateKey clientPrivateKey = keypair.getPrivate();
        PublicKey clientPublicKey = keypair.getPublic();

        // ...and a CSR from them...
        PKCS10CertificationRequest csr = generateRequest(x509DN, clientPublicKey, clientPrivateKey, keyType);

        // ...sign it
        KeyStore rootKS = loadRootKeyStore();
        X509Certificate rootCert = (X509Certificate) rootKS.getCertificate(rootCAKeystoreAlias);
        if (rootCert == null) {
            logger.error("Can't get root certificate from CA keystore");
            return null;
        }
        PrivateKey rootPrivKey = (PrivateKey) rootKS.getKey(rootCAKeystoreAlias,
                rootCAKeystorePassword.toCharArray());
        X509Certificate[] signedChain = createSignedCert(rootCert, rootPrivKey, csr, keyType);

        //...package up the result...
        CABean caBean = new CABean();
        caBean.setChain(signedChain);
        caBean.setCSRPrivateKey(clientPrivateKey);
        caBean.setSubjectDN(x509DN);

        // ...and send it back
        return caBean;
    } catch (Exception e) {
        logger.error(e);
        return null;
    }
}

From source file:be.fgov.kszbcss.rhq.websphere.connector.agent.ConnectorSubsystemComponent.java

public OperationResult invokeOperation(String name, Configuration parameters)
        throws InterruptedException, Exception {
    if (name.equals("importCertificateFromFile")) {
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        InputStream in = new FileInputStream(parameters.getSimple("file").getStringValue());
        try {//w w  w .  ja  v a2 s  .c o m
            Iterator<? extends Certificate> it = cf.generateCertificates(in).iterator();
            if (it.hasNext()) {
                TrustStoreManager.getInstance().addCertificate(parameters.getSimple("alias").getStringValue(),
                        (X509Certificate) it.next());
            } else {
                throw new Exception("No certificate found");
            }
        } finally {
            in.close();
        }
        return null;
    } else if (name.equals("retrieveCellCertificate")) {
        DeploymentManager dm = new DeploymentManager(null, new ConfigurationBasedProcessLocator(parameters));
        String cell = dm.getCell();
        ConfigQueryExecutor configQueryExecutor = ConfigQueryServiceFactory.getInstance()
                .getConfigQueryExecutor(dm);
        try {
            X509Certificate cert = configQueryExecutor.query(CellRootCertificateQuery.INSTANCE);
            TrustStoreManager.getInstance().addCertificate("cell:" + cell, cert);
        } finally {
            configQueryExecutor.destroy();
        }
        return null;
    } else if (name.equals("retrieveCertificateFromPort")) {
        SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(new KeyManager[0],
                new TrustManager[] {
                        new AutoImportTrustManager(parameters.getSimple("alias").getStringValue()) },
                new SecureRandom());
        SSLSocket socket = (SSLSocket) sslContext.getSocketFactory().createSocket(
                parameters.getSimple("host").getStringValue(), parameters.getSimple("port").getIntegerValue());
        try {
            socket.startHandshake();
        } finally {
            socket.close();
        }
        return null;
    } else if (name.equals("listCertificates")) {
        final PropertyList certificates = new PropertyList("certificates");
        TrustStoreManager.getInstance().execute(new TrustStoreAction() {
            public void execute(KeyStore truststore) throws Exception {
                // Sort the aliases for convenience
                Set<String> aliases = new TreeSet<String>();
                for (Enumeration<String> e = truststore.aliases(); e.hasMoreElements();) {
                    aliases.add(e.nextElement());
                }
                for (String alias : aliases) {
                    X509Certificate cert = (X509Certificate) truststore.getCertificate(alias);
                    PropertyMap map = new PropertyMap("certificate");
                    map.put(new PropertySimple("alias", alias));
                    map.put(new PropertySimple("subject", cert.getSubjectDN().toString()));
                    MessageDigest md = MessageDigest.getInstance("SHA-1");
                    md.update(cert.getEncoded());
                    byte[] digest = md.digest();
                    StringBuilder fingerprint = new StringBuilder();
                    for (int i = 0; i < digest.length; i++) {
                        if (i > 0) {
                            fingerprint.append(':');
                        }
                        fingerprint.append(getHexDigit(((int) digest[i] & 0xf0) >> 4));
                        fingerprint.append(getHexDigit((int) digest[i] & 0x0f));
                    }
                    map.put(new PropertySimple("fingerprint", fingerprint.toString()));
                    certificates.add(map);
                }
            }
        }, true);
        if (log.isDebugEnabled()) {
            log.debug("certificates=" + certificates);
        }
        OperationResult result = new OperationResult();
        result.getComplexResults().put(certificates);
        return result;
    } else if (name.equals("removeCertificate")) {
        final String alias = parameters.getSimple("alias").getStringValue();
        TrustStoreManager.getInstance().execute(new TrustStoreAction() {
            public void execute(KeyStore truststore) throws Exception {
                truststore.deleteEntry(alias);
            }
        }, false);
        return null;
    } else if (name.equals("renameCertificate")) {
        final String oldAlias = parameters.getSimple("oldAlias").getStringValue();
        final String newAlias = parameters.getSimple("newAlias").getStringValue();
        TrustStoreManager.getInstance().execute(new TrustStoreAction() {
            public void execute(KeyStore truststore) throws Exception {
                Certificate cert = truststore.getCertificate(oldAlias);
                truststore.setCertificateEntry(newAlias, cert);
                truststore.deleteEntry(oldAlias);
            }
        }, false);
        return null;
    } else {
        return null;
    }
}

From source file:org.atricore.idbus.capabilities.sso.support.test.XmlDsigTest.java

/**
 * Sign a SAMLR2 Assertion using the configured JSR 105 Provider
 *//*w ww. j  a v a  2 s. c  o  m*/
@Test
public void assertionSign() throws Exception {
    //All the parameters for the keystore
    String keystoreType = "JKS";
    String keystoreFile = "src/test/resources/keystore.jks";
    String keystorePass = "xmlsecurity";
    String privateKeyAlias = "test";
    String privateKeyPass = "xmlsecurity";
    String certificateAlias = "test";
    File assertionFile = new File("src/test/resources/assertion-001.xml");
    File signatureFile = new File("target/assertion-signed-001.xml");

    JAXBContext context = JAXBContext.newInstance("oasis.names.tc.saml._2_0.assertion");
    Unmarshaller um = context.createUnmarshaller();

    JAXBElement jaxbElement = (JAXBElement) um.unmarshal(assertionFile);

    AssertionType assertion = (AssertionType) jaxbElement.getValue();

    // Unmarshall the assertion
    KeyStore ks = KeyStore.getInstance(keystoreType);
    FileInputStream fis = new FileInputStream(keystoreFile);

    //load the keystore
    ks.load(fis, keystorePass.toCharArray());

    //get the private key for signing.
    PrivateKey privateKey = (PrivateKey) ks.getKey(privateKeyAlias, privateKeyPass.toCharArray());

    X509Certificate cert = (X509Certificate) ks.getCertificate(certificateAlias);
    PublicKey publicKey = cert.getPublicKey();

    // Create a DOM XMLSignatureFactory that will be used to generate the
    // enveloped signature
    String providerName = System.getProperty("jsr105Provider", "org.jcp.xml.dsig.internal.dom.XMLDSigRI");

    XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM",
            (Provider) Class.forName(providerName).newInstance());

    // Create a Reference to the enveloped document (in this case we are
    // signing the whole document, so a URI of "" signifies that) and
    // also specify the SHA1 digest algorithm and the ENVELOPED Transform.
    Reference ref = fac.newReference("#" + assertion.getID(), fac.newDigestMethod(DigestMethod.SHA1, null),
            Collections.singletonList(fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)),
            null, null);

    // Create the SignedInfo
    SignedInfo si = fac.newSignedInfo(
            fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS,
                    (C14NMethodParameterSpec) null),
            fac.newSignatureMethod(SignatureMethod.DSA_SHA1, null), Collections.singletonList(ref));

    // Instantiate the document to be signed
    javax.xml.parsers.DocumentBuilderFactory dbf = javax.xml.parsers.DocumentBuilderFactory.newInstance();

    //XML Signature needs to be namespace aware
    dbf.setNamespaceAware(true);

    javax.xml.parsers.DocumentBuilder db = dbf.newDocumentBuilder();
    org.w3c.dom.Document doc = db.newDocument();

    Marshaller m = context.createMarshaller();
    m.marshal(jaxbElement, doc);

    // Create a DOMSignContext and specify the DSA PrivateKey and
    // location of the resulting XMLSignature's parent element
    DOMSignContext dsc = new DOMSignContext(privateKey, doc.getDocumentElement(),
            doc.getDocumentElement().getFirstChild());

    // Create the XMLSignature (but don't sign it yet)
    KeyInfoFactory kif = fac.getKeyInfoFactory();

    X509Data kv = kif.newX509Data(Collections.singletonList(cert));

    // Create a KeyInfo and add the KeyValue to it
    KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv));

    javax.xml.crypto.dsig.XMLSignature signature = fac.newXMLSignature(si, ki);

    signature.sign(dsc);
    // output the resulting document

    FileOutputStream f = new FileOutputStream(signatureFile);
    XMLUtils.outputDOMc14nWithComments(doc, f);
    f.close();
}

From source file:org.wso2.carbon.apimgt.impl.utils.CertificateMgtUtils.java

/**
 * Method to get the information of the certificate.
 *
 * @param alias : Alias of the certificate which information should be retrieved
 * @return : The details of the certificate as a MAP.
 */// w w  w .ja  va  2s  .  c om
public CertificateInformationDTO getCertificateInformation(String alias) throws CertificateManagementException {

    CertificateInformationDTO certificateInformation = new CertificateInformationDTO();
    File trustStoreFile = new File(TRUST_STORE);
    try {
        localTrustStoreStream = new FileInputStream(trustStoreFile);
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        trustStore.load(localTrustStoreStream, TRUST_STORE_PASSWORD);

        if (trustStore.containsAlias(alias)) {
            X509Certificate certificate = (X509Certificate) trustStore.getCertificate(alias);
            certificateInformation = getCertificateMetaData(certificate);
        }
    } catch (IOException e) {
        throw new CertificateManagementException("Error wile loading the keystore.", e);
    } catch (CertificateException e) {
        throw new CertificateManagementException("Error loading the keystore from the stream.", e);
    } catch (NoSuchAlgorithmException e) {
        throw new CertificateManagementException("Could not find the algorithm to load the certificate.", e);
    } catch (KeyStoreException e) {
        throw new CertificateManagementException("Error reading certificate contents.", e);
    } finally {
        closeStreams(localTrustStoreStream);
    }
    return certificateInformation;
}

From source file:org.apache.hadoop.yarn.server.resourcemanager.security.MockX509SecurityHandler.java

private void verifyContentOfAppTrustStore(byte[] appTrustStore, char[] password, String appUser,
        ApplicationId appId) throws GeneralSecurityException, IOException {
    File trustStoreFile = Paths.get(systemTMP, appUser + "-" + appId.toString() + "_tstore.jks").toFile();
    boolean certificateMissing = false;

    try {//from   w ww .jav a  2 s  .  c o  m
        KeyStore systemTrustStore = loadSystemTrustStore(getConfig());
        FileUtils.writeByteArrayToFile(trustStoreFile, appTrustStore, false);
        KeyStore ts = KeyStore.getInstance("JKS");
        try (FileInputStream fis = new FileInputStream(trustStoreFile)) {
            ts.load(fis, password);
        }

        Enumeration<String> sysAliases = systemTrustStore.aliases();
        while (sysAliases.hasMoreElements()) {
            String alias = sysAliases.nextElement();

            X509Certificate appCert = (X509Certificate) ts.getCertificate(alias);
            if (appCert == null) {
                certificateMissing = true;
                break;
            }

            X509Certificate sysCert = (X509Certificate) systemTrustStore.getCertificate(alias);
            if (!Arrays.equals(sysCert.getSignature(), appCert.getSignature())) {
                certificateMissing = true;
                break;
            }
        }
    } finally {
        FileUtils.deleteQuietly(trustStoreFile);
        assertFalse(certificateMissing);
    }
}

From source file:org.atricore.idbus.capabilities.sso.support.test.XmlDsigTest.java

/**
 * Sign a simple DOM document using the configured JSR 105 Provider
 *//*from  www. j av  a2 s.  c  o m*/
@Test
public void simpleDocumentSign() throws Exception {

    //All the parameters for the keystore
    String keystoreType = "JKS";
    String keystoreFile = "src/test/resources/keystore.jks";
    String keystorePass = "xmlsecurity";
    String privateKeyAlias = "test";
    String privateKeyPass = "xmlsecurity";
    String certificateAlias = "test";
    File signatureFile = new File("target/signature.xml");

    KeyStore ks = KeyStore.getInstance(keystoreType);
    FileInputStream fis = new FileInputStream(keystoreFile);

    //load the keystore
    ks.load(fis, keystorePass.toCharArray());

    //get the private key for signing.
    PrivateKey privateKey = (PrivateKey) ks.getKey(privateKeyAlias, privateKeyPass.toCharArray());

    X509Certificate cert = (X509Certificate) ks.getCertificate(certificateAlias);
    PublicKey publicKey = cert.getPublicKey();

    // Create a DOM XMLSignatureFactory that will be used to generate the
    // enveloped signature
    String providerName = System.getProperty("jsr105Provider", "org.jcp.xml.dsig.internal.dom.XMLDSigRI");

    XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM",
            (Provider) Class.forName(providerName).newInstance());

    // Create a Reference to the enveloped document (in this case we are
    // signing the whole document, so a URI of "" signifies that) and
    // also specify the SHA1 digest algorithm and the ENVELOPED Transform.
    Reference ref = fac.newReference("#12345", fac.newDigestMethod(DigestMethod.SHA1, null),
            Collections.singletonList(fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)),
            null, null);

    // Create the SignedInfo
    SignedInfo si = fac.newSignedInfo(
            fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS,
                    (C14NMethodParameterSpec) null),
            fac.newSignatureMethod(SignatureMethod.DSA_SHA1, null), Collections.singletonList(ref));

    // Instantiate the document to be signed
    javax.xml.parsers.DocumentBuilderFactory dbf = javax.xml.parsers.DocumentBuilderFactory.newInstance();

    //XML Signature needs to be namespace aware
    dbf.setNamespaceAware(true);

    javax.xml.parsers.DocumentBuilder db = dbf.newDocumentBuilder();
    org.w3c.dom.Document doc = db.newDocument();

    //Build a sample document. It will look something like:
    //<!-- Comment before -->
    //<apache:RootElement xmlns:apache="http://www.apache.org/ns/#app1" ID="12345">Some simple text
    //</apache:RootElement>
    //<!-- Comment after -->
    doc.appendChild(doc.createComment(" Comment before "));

    Element root = doc.createElementNS("http://www.apache.org/ns/#app1", "apache:RootElement");

    root.setAttributeNS(null, "ID", "12345");

    root.setAttributeNS(null, "attr1", "test1");
    root.setAttributeNS(null, "attr2", "test2");
    root.setAttributeNS(org.apache.xml.security.utils.Constants.NamespaceSpecNS, "xmlns:foo",
            "http://example.org/#foo");
    root.setAttributeNS("http://example.org/#foo", "foo:attr1", "foo's test");

    root.setAttributeNS(org.apache.xml.security.utils.Constants.NamespaceSpecNS, "xmlns:apache",
            "http://www.apache.org/ns/#app1");
    doc.appendChild(root);
    root.appendChild(doc.createTextNode("Some simple text\n"));

    // Create a DOMSignContext and specify the DSA PrivateKey and
    // location of the resulting XMLSignature's parent element
    DOMSignContext dsc = new DOMSignContext(privateKey, doc.getDocumentElement());

    // Create the XMLSignature (but don't sign it yet)
    KeyInfoFactory kif = fac.getKeyInfoFactory();

    X509Data kv = kif.newX509Data(Collections.singletonList(cert));

    // Create a KeyInfo and add the KeyValue to it
    KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv));
    javax.xml.crypto.dsig.XMLSignature signature = fac.newXMLSignature(si, ki);

    signature.sign(dsc);

    // TODO : Verify signature ?

    // output the resulting document
    FileOutputStream f = new FileOutputStream(signatureFile);
    XMLUtils.outputDOMc14nWithComments(doc, f);
    f.close();

}

From source file:test.integ.be.fedict.commons.eid.client.JCATest.java

@Test
public void testPSS256() throws Exception {
    Security.addProvider(new BeIDProvider());
    Security.addProvider(new BouncyCastleProvider());
    KeyStore keyStore = KeyStore.getInstance("BeID");
    keyStore.load(null);/* w ww . ja v a 2  s  .  c o m*/
    PrivateKey authnPrivateKey = (PrivateKey) keyStore.getKey("Authentication", null);
    X509Certificate authnCertificate = (X509Certificate) keyStore.getCertificate("Authentication");
    PublicKey authnPublicKey = authnCertificate.getPublicKey();

    Signature signature = Signature.getInstance("SHA256withRSAandMGF1");
    signature.initSign(authnPrivateKey);

    byte[] toBeSigned = "hello world".getBytes();
    signature.update(toBeSigned);
    byte[] signatureValue = signature.sign();

    signature.initVerify(authnPublicKey);
    signature.update(toBeSigned);
    boolean result = signature.verify(signatureValue);
    assertTrue(result);
}

From source file:com.predic8.membrane.core.transport.ssl.SSLContext.java

private KeyStore openKeyStore(Store store, String defaultType, char[] keyPass, ResolverMap resourceResolver,
        String baseLocation) throws NoSuchAlgorithmException, CertificateException, FileNotFoundException,
        IOException, KeyStoreException, NoSuchProviderException {
    String type = store.getType();
    if (type == null)
        type = defaultType;//from w  w  w. j a va2s . c om
    char[] password = keyPass;
    if (store.getPassword() != null)
        password = store.getPassword().toCharArray();
    if (password == null)
        throw new InvalidParameterException("Password for key store is not set.");
    KeyStore ks;
    if (store.getProvider() != null)
        ks = KeyStore.getInstance(type, store.getProvider());
    else
        ks = KeyStore.getInstance(type);
    ks.load(resourceResolver.resolve(ResolverMap.combine(baseLocation, store.getLocation())), password);
    if (!default_certificate_warned && ks.getCertificate("membrane") != null) {
        byte[] pkeEnc = ks.getCertificate("membrane").getEncoded();
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        md.update(pkeEnc);
        byte[] mdbytes = md.digest();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < mdbytes.length; i++) {
            if (i > 0)
                sb.append(':');
            sb.append(Integer.toString((mdbytes[i] & 0xff) + 0x100, 16).substring(1));
        }
        if (sb.toString().equals(DEFAULT_CERTIFICATE_SHA256)) {
            log.warn("Using Membrane with the default certificate. This is highly discouraged! "
                    + "Please run the generate-ssl-keys script in the conf directory.");
            default_certificate_warned = true;
        }
    }
    return ks;
}

From source file:org.eclipse.gyrex.http.jetty.internal.admin.CertificateDefinition.java

@Override
public String getInfo() {
    try {/*from  w w  w.  j  av  a2 s  .co m*/
        final StrBuilder certInfo = new StrBuilder();
        final KeyStore ks = getKeyStore();
        final Enumeration aliases = ks.aliases();
        while (aliases.hasMoreElements()) {
            final String alias = (String) aliases.nextElement();
            if (!certInfo.isEmpty()) {
                certInfo.append(", ");
            }
            //            certInfo.append(alias).append(": ");
            if (ks.isKeyEntry(alias)) {
                Certificate[] chain = ks.getCertificateChain(alias);
                if (null == chain) {
                    final Certificate certificate = ks.getCertificate(alias);
                    chain = new Certificate[] { certificate };
                }
                for (int i = 0; i < chain.length; i++) {
                    if (i > 0) {
                        certInfo.append(" ");
                    }
                    final Certificate certificate = chain[i];
                    if (certificate instanceof X509Certificate) {
                        final X509Certificate x509 = (X509Certificate) certificate;
                        final X500PrincipalHelper helper = new X500PrincipalHelper(
                                x509.getSubjectX500Principal());
                        certInfo.append(helper.getCN());
                        certInfo.append(", valid till ").append(TO_STRING_FORMAT.format(x509.getNotAfter()));
                    } else {
                        certInfo.append("INVALID");
                    }
                }
            } else {
                certInfo.append("IGNORED");
            }
        }
        return StringUtils.trim(certInfo.toString());
    } catch (final Exception e) {
        return ExceptionUtils.getRootCauseMessage(e);
    }
}