Cryptographic Module

The Cryptographic Module provides a dedicated API for guaranteeing integrity and confidentiality.

Code Reference

The most important interfaces are:

  • The PasswordCryptoService interface is a service allowing users to encrypt and decrypt text using a password. You can access this service from Velocity with $services.crypto.passwd.
  • The X509CryptoService interface is a service allowing components to sign text, determine the validity and signer of already signed text, create keys and register new certificates. The service can be accessed from Velocity with $services.crypto.x509.

The most important classes are:

Examples

Password Protect

In order to be able to check a password or a token without keeping the original copy, you can use the PasswordCryptoService interface and more specifically the below methods:

  • String protectPassword(final String password); does a hash of the password with a hash function specifically designed to make password guessing attacks difficult. This hash does salting and multiple iterations and thus uses a large and configurable amount of processor time and memory. The method can be accessed from Velocity with $services.crypto.passwd.protectPassword().
  • boolean isPasswordCorrect(final String password, final String protectedPassword); checks the validity of a password and it can be accessed from Velocity with $services.crypto.passwd.isPasswordCorrect().
{{velocity}}
{{html}}
<form action='$doc.getURL()' method='post'>
  #if("$!request.getParameter('passwd')" != '')
    #if("$!request.getParameter('safePasswd')" != '' && "$!request.getParameter('validate')" != '')
      #if($services.crypto.passwd.isPasswordCorrect($request.getParameter('passwd'), $request.getParameter('safePasswd')))
        You win the internet!
      #else
        Wrong Password.
        #set($safePasswd = $!escapetool.xml($!request.getParameter('safePasswd')))
      #end
    #else
      #set($safePasswd = $services.crypto.passwd.protectPassword($request.getParameter('passwd')))
    #end
  #end
 <br/>
 <label for="passwd">Password</label>
 <br/>
 <input type="text" name="passwd" id="passwd" />
 <br/>
 <label for="safePasswd">Protected password</label>
 <br/>
 <textarea id="safePasswd" name="safePasswd" cols=50 rows=10>$!safePasswd</textarea>
 <br/>
 <input type="submit" value="Protect Password" />
 <input type="submit" name="validate" value="Validate Password" />
</form>
{{/html}}
{{/velocity}}

Encipher a Text Using a Password

You can encipher text using the same strong key derivation function which is used for protection of passwords. By default, the text is enciphered with CAST-5, a strong 128 bit cipher. You can choose to switch to "AESPasswordCiphertext" which uses the AES cipher engine with a 128 bit key, by editing the crypto.passwd.passwordCiphertext parameter in the "WEF-INF/xwiki.properties" configuration file: #crypto.passwd.passwordCiphertext = CAST5PasswordCiphertext

Another 2 important methods of the PasswordCryptoService interface are:

  • String encryptText(final String plaintext, final String password); enciphers the given text with the password. The same password will be able to decipher it. The method can be accessed from Velocity with $services.crypto.passwd.encryptText().
  • String decryptText(final String base64Ciphertext, final String password); decrypts a piece of text encrypted with encryptText() and it can be accessed from Velocity with $services.crypto.passwd.decryptText().
{{velocity}}
{{html}}
##
## Some browsers convert NL line termination to CR-NL which causes trouble with recognition of the header.
## This workaround is only needed if the encrypted text is being posted from a web browser.
#set($ciph = $!request.getParameter('ciphertext').replaceAll('\r\n', $util.getNewline()))
##
<form action='$doc.getURL()' method='post'>
  #if("$!request.getParameter('ciphertext')" != '' && "$!request.getParameter('decrypt')" != '')
    #set($plaintext =
      $services.crypto.passwd.decryptText($ciph, $request.getParameter('passwd')))
    ## decryptText returns null if the password is wrong.
    #if(!$plaintext)
      Wrong Password.
      #set($ciphertext = $!escapetool.xml($!request.getParameter('ciphertext')))
    #end
  #elseif("$!request.getParameter('plaintext')" != '')
    #set($ciphertext =
      $services.crypto.passwd.encryptText($request.getParameter('plaintext'), $request.getParameter('passwd')))
  #end
 <br/>
 <label for="passwd">Password</label>
 <br/>
 <input type="text" name="passwd" id="passwd" />
 <br/>
 <label for="plaintext">Plaintext</label>
 <br/>
 <textarea id="plaintext" name="plaintext" cols=50 rows=10>$!escapetool.xml($!plaintext)</textarea>
 <br/>
 <label for="ciphertext">Ciphertext</label>
 <br/>
 <textarea id="ciphertext" name="ciphertext" cols=50 rows=10>$!ciphertext</textarea>
 <br/>
 <input type="submit" value="Encrypt" />
 <input type="submit" name="decrypt" value="Decrypt" />
</form>
{{/html}}
{{/velocity}}

Sign Data in the Browser

Some browsers support a JavaScript function named crypto.signText() which returns a string of encoded data representing a signed object. The JavaScript code asks the user to PKCS#7 sign a piece of text using a client certificate in the browser certificate store. 

Similarly, the XWiki Cryptographic Module has a matching method of the X509CryptoService interface named XWikiX509Certificate verifyText(final String signedText, final String base64Signature); which verifies a pkcs#7 signature and returns the certificate of the user who signed it. This doesn't mean the certificate is trusted, only that the signature is valid. The function can be accessed from Velocity using $services.crypto.x509.verifyText().

{{velocity}}
#if("$!request.getParameter('text')" != '')
  #if("$!request.getParameter('signature')" != '')
    #set($userCert = $services.crypto.x509.verifyText($request.getParameter('text'), $request.getParameter('signature')))
    #if($userCert)
      ## Invalid signature causes the function to retirn null.
      Signature is valid and the certificate which signed it is:
      $userCert
    #end
  #else
    Browser doesn't support javascript, couldn't sign text.
  #end
#else
  ## Help the user sign a piece of text.
  {{html}}
 <form id="signForm" action='$doc.getURL()' method='post'>
 <textarea id="textField" cols=50 rows=10 name="text">Put some text here, it will be signed.</textarea>
 <input type="hidden" id="signatureField" name="signature" value="" />
 <br/>
 <input type="submit" id="sendButton" value="Sign and Send" />
 </form>
 <script>
    Event.observe(document, 'xwiki:dom:loaded', function() {
     var button = document.getElementById('sendButton');
      Event.observe(button, 'click', function(event) {
       var text = document.getElementById('textField');
       var signature = document.getElementById('signatureField');
       if (window.crypto == undefined || window.crypto.signText == undefined) {
          alert("You browser doesn't support signing.");
          Event.stop(event);
        } else {
          signature.value = crypto.signText(text.value, "ask");
        }
      });
    });
 </script>
  {{/html}}
#end
{{/velocity}}

The above snippet only works when the client already has a certificate. 

Generate Client Certificates to Sign With

To generate a client certificate, you can use the String signText(final String textToSign, final XWikiX509KeyPair toSignWith, final String password); method of the X509CryptoService interface which produces a pkcs#7 signature for the given text that will be then signed with the key belonging to the author of the code calling the method. In order for us to be able to actually use signText(), we will need the HTML keygen tag for generating the key pair and submitting the public key as part of the HTML form. 

The user certificate must have a signature which is trusted by the browser. The XWiki Cryptographic Module generates a certificate authority each time a new user certificate is needed, so the user can install it. To help the user generate a client certificate and install a matching authority certificate, we will use the XWikiX509Certificate[] certsFromSpkac(final String spkacSerialization, final int daysOfValidity); method which creates an array of Base64 encoded DER formatted X509Certificates containing:

  •  A certificate from the given SPKAC
  •  A certificate authority certificate which will validate the first certificate in the array.

This method can be accessed from Velocity using $services.crypto.x509.certsFromSpkac().

{{velocity}}
#if("$!request.getParameter('spkac')" != '')
  ## We'll make it valid for 365 days.
  #set($certs = $services.crypto.x509.certsFromSpkac($request.getParameter('spkac'), 365))
  ## Save the authority cert as an attachment.
  $doc.addAttachment('authority.crt', $certs.get(1).getEncoded())
  $doc.save('Added certificate authority.')
  ## Write the resulting cert back as a DER encoded string.
  $response.setContentType("application/x-x509-user-cert")
  #set($certDER = $certs.get(0).getEncoded())
  $response.getOutputStream().write($certDER)
  $context.setFinished(true)
#else
  {{html clean="false"}}
 <form id="keyForm" action='$doc.getURL()' method='post'>
  <keygen name="spkac" />
  <input type="submit" value="Generate Certificate" />
 </form>
 <br/>
 <a id="authCertLink" href="$doc.getAttachmentURL('authority.crt')" class="hidden">Please click here to install the authority certificate.</a>
 <script>
   function tryRequest()
    {
     try {
       new Ajax.Request("$doc.getAttachmentURL('authority.crt')", {
          method: 'get',
          onSuccess: function() {
            $('authCertLink').removeAttribute('class');
          },
          onFailure: function() {
            setTimeout('tryRequest()', 2000);
          }
        });
      } catch (exception) {
       // Try again.
       setTimeout("tryRequest()", 2000);
      }
    }

    Event.observe(document, 'xwiki:dom:loaded', function() {
      Event.observe($('keyForm'), 'submit', function() {
        setTimeout('tryRequest()', 2000);
      });
    });
 </script>
  {{/html}}
#end
{{/velocity}}

Related Pages

Search this space

 

Most popular tags

Failed to execute the [groovy] macro
  1. access rights
  2. activity stream
  3. annotation
  4. attachment
  5. comment
  6. Document Tree Macro
  7. export
  8. Extension Manager
  9. Flamingo skin
  10. global user
  11. Groovy event listener
  12. group
  13. nested page
  14. search
  15. skin
  16. syntax
  17. user
  18. user profile
  19. velocity macros
  20. wiki
  21. wysiwyg
  22. XWiki Applications
  23. xwikiattachment_archive table
  24. xwikiattachment table
  25. xwikiattrecyclebin table
  26. xwikiproperties table

[Display all tags from this space]