Environment setup ================= As described in the :ref:`security_auth` chapter, the relationship between the in-browser JavaScript code and the SCWS service must be established using a specific :ref:`webapp_certificate` provided by Idopte, and a challenge-response mutual authentication scheme. The functions decribed below must be called before any other API methods to achieve this. Client-side ----------- .. js:autofunction:: SCWS.findService .. js:autofunction:: SCWS.createEnvironment Below is an example usage of these functions, with the call to specific server-side ``generatechallenge``and ``verifycryptogramsignchallenge`` scripts to generate a challenge, verify the cryptogram returned back and produce the required signature of challenge sent by local service: .. caution:: Note that cryptogram verification and challenge signature are done in a unique script, which does not insure compatibility with middleware/SCWS API versions prior to 6.23.25.0 and 2.11, respectively. If you wish to insure compatibility with previous versions and do not need to use high level SCWS entry points (requestCertificates/requestPrivateKey and SignEncrypt entry points), you should use the old method for environment creation (one-sided authentication instead of mutual). You can find sample codes here: :ref:`oldenvsetupsamples`. If you do not need to insure compatibility and you wish to use the newest SCWS functions, you should use sample codes given below. .. code-block:: js var webappcert = "4HfLHzMS05>]T+PeNPwQ=?r-f-mJ4nre!!s7cqOJ0/Os@Kw?HHaS>ewC+WPnDEbhxh:6MUtZIt0+D^Wa2eO?(&l.>A0MDw!JF2K0[8TM{W^[FLaq?oRb{WDRmEmo#oEoV3e= $pubKeysLen) { http_response_code(400); } else { $k = $pubKeys[$pubKeyID]; if (str_contains($k, "!REVOKED!")) { http_response_code(400); } else { $challenge = $_SESSION['challenge']; $output = ""; openssl_public_decrypt(hex2bin($_GET["cryptogram"]), $output, openssl_pkey_get_public($k)); if ($output == $challenge) { $k = << ``verifycryptogamsignchallenge.java``: .. code-block:: Java final String privateKeyPEM = "-----BEGIN RSA PRIVATE KEY-----\r\n" + "MIIBOgIBAAJBAMPMNNpbZZddeT/GTjU0PWuuN9VEGpxXJTAkmZY02o8238fQ2ynt\r\n" + "N40FVl08YksWBO/74XEjU30mAjuaz/FB2kkCAwEAAQJBALoMlsROSLCWD5q8EqCX\r\n" + "rS1e9IrgFfEtFZczkAWc33lo3FnFeFTXSMVCloNCBWU35od4zTOhdRPAWpQ1Mzxi\r\n" + "aCkCIQD9qjKjNvbDXjUcCNqdiJxPDlPGpa78yzyCCUA/+TNwVwIhAMWZoqZO3eWq\r\n" + "SCBTLelVQsg6CwJh9W7vlezvWxUni+ZfAiAopBAg3jmC66EOsMx12OFSOTVq6jiy\r\n" + "/8zd+KV2mnKHWQIgVpZiLZo1piQeAvwwDCUuZGr61Ap08C3QdsjUEssHhOUCIBee\r\n" + "72JZuJeABcv7lHhAWzsiCddVAkdnZKUo6ubaxw3u\r\n" + "-----END RSA PRIVATE KEY-----"; @GetMapping("/verifycryptogramsignchallenge") public String verifyCryptogramSignChallenge(Integer keyID, String cryptogram, String rnd, HttpServletRequest request) { try { // Parse IdoptePublicKeys file and retrieve the public key to use for verification String pubKeysStr = new String(Files.readAllBytes(Paths.get("IdoptePublicKeys"))); Pattern pattern = Pattern.compile("-----BEGIN PUBLIC KEY-----(.|\\n|\\r)*?-----END PUBLIC KEY-----"); Matcher matcher = pattern.matcher(pubKeysStr); List matches = new ArrayList<>(); while (matcher.find()) { matches.add(matcher.group()); } if (keyID >= matches.size()) { throw new Exception("Invalid key ID"); } String publicIdoKeyPEM = matches.get(keyID); if (publicIdoKeyPEM.contains("!REVOKED!")) { throw new Exception("Public key revoked"); } // Prepare public key KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PemReader reader = new PemReader(new StringReader(publicIdoKeyPEM)); byte[] pubKey = reader.readPemObject().getContent(); X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(pubKey); PublicKey publicKey = keyFactory.generatePublic(publicKeySpec); // Retrieve challenge generated in generateChallenge stored in session variable HttpSession session = request.getSession(); String challengeHex = session.getAttribute("challenge").toString(); // Verify the cryptogram Signature verifier = Signature.getInstance("NONEwithRSA"); verifier.initVerify(publicKey); verifier.update(Hex.decode(challengeHex)); boolean verification = verifier.verify(Hex.decode(cryptogram)); if (!verification) { throw new Exception("Cryptogram verification failed"); } else { PrivateKey privateKey; try (PEMParser pemParser = new PEMParser(new StringReader(privateKeyPEM))) { JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC"); Object object = pemParser.readObject(); KeyPair kp = converter.getKeyPair((PEMKeyPair)object); privateKey = kp.getPrivate(); } Signature signer = Signature.getInstance("NONEwithRSA","BC"); signer.initSign(privateKey); signer.update(Hex.decode(rnd)); byte[] signature = signer.sign(); return Hex.toHexString(signature); } } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e.getMessage(), e); } } For this script to work, the ``IdoptePublicKeys`` file provided by Idopte is required. The file format is described in the :ref:`security_auth` chapter. Note that the file may evolve, but it should always be parsed in the way it is done in this sample code. The key included in these scripts represents the :ref:`security_auth` key generated on the customer's side, whose public key is provided to Idopte when requesting the :ref:`webapp_certificate`. .. caution:: Note that above scripts do not check the origin of the request. This is volontary, as ensuring that the request effectively comes from a valid client is the responsibility of the web application developer, and depends on the architecture of the entire application. However, not checking the origin of the request before producing or verifying a signature is a security weakness and should be avoided. Standard methods such as cookies, or session mechanisms, can be used to solve this requirement.