Cryptographic Module

Last modified by Vincent Massol on 2013/03/11 09:40

cogProvides APIs for both integrity and confidentiality
TypeJAR
Developed by

XWiki Development Team

Rating
Rate!
1 Votes
LicenseGNU Lesser General Public License 2.1
Bundled With

XWiki Enterprise, XWiki Enterprise Manager

Compatibility

Since XWiki Enterprise 2.5M1

Description

With today's increasingly complex software running on servers and the unprecedented level of trust placed in the same servers, the deck is certainly stacked in favor of those who would use our computers against us. The science of cryptography promises to help us maintain control of our computers by reducing the amount of code and number of systems which must be trusted.

Who do you trust?

Security is about trust, the more code you trust the more code you must review in a security audit. This increases the cost of a full security audit which pushes far too many companies to forsake the full security audit in favor of hiring a security professional to spend a week attacking their systems. If this security professional does not succeed then it is assumed that the system is secure. This "I don't see it so it isn't there" attitude toward security has lead to a large number of real world security breaches by methods which were found to be astonishingly primitive.

What is untrusted code?
Untrusted code is software which no matter how badly it behaves, it cannot violate the user's security requirements. This code does not need to be reviewed in a security audit.

How does cryptography affect trust?

Cryptography helps decrease the amount of trusted code in a system by allowing trusted and even confidential information to be handled by untrusted code. If Alice is passing important instructions to Bob and it will be handled by George, Alice can sign that message and as long as Bob has a true copy of Alice's public key, he can ensure that the instruction was not tampered with. Then George can be excluded from the next security audit.

Can proprietary software be trusted?
Though proprietary software is useful for many things, you can only trust software who's source code you are licensed to review.

What does the XWiki Cryptographic Module have to offer?

The XWiki Cryptographic module provides tools for both integrity and confidentiality.

Protection (1 way hashing) of passwords

If you need to be able to verify a password or other token without keeping an original copy which might be compromised, you can use the password verification function. What sets the password verification function apart from a simple hash like MD-5 or SHA-1 is that the password verification function uses a large and configurable amount of processor time and memory to validate the password. This means the system administrator can decide how difficult a password guessing attack should be. By default, the password verification function uses the Scrypt key derivation function developed by Colin Percival, deputy security officer for FreeBSD operating system.

How to use $services.crypto.passwd.protectPassword() and $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}}
Remember, the passwords are randomly salted so each time protectPassword is called with the same input, the result will be different.

Encipherment of 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 AES-128 by editing your xwiki.properties file.

How to use $services.crypto.passwd.encryptText() and $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}}

Signing data in the browser

Browsers such as Firefox and Opera support a Javascript function called crypto.signText() (more information here) which allows javascript code to ask a user to PKCS#7 sign a piece of text using a client certificate in the browser's certificate store (This feature is also supported by Microsoft IE, but using it is more complex). XWiki Cryptographic Module has a matching verifyText() function which will make sure the results provided by signText are valid and return the certificate which signed it. Note that it does not make sure the certificate is trusted, only that the signature is valid.

How to use $services.crypto.x509.verifyText():

This will only work if the user already has a client certificate.
{{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}}

Acquiring client certificates to sign with

If your users already have their own certificates, you can skip down to the part about importing them from PEM format. Otherwise you will want to know how you can easily generate certificates to verify user signatures.

Generating client certificates

In order to make use of crypto.signText, the user needs to have a client certificate to sign with. Fortunately, web browsers make this easy and secure as well. By using the HTML <keygen> tag which has long existed in most browsers and is now standardized in HTML5, you can get the browser to create a key pair and send only the public key to the server. The private key never leaves the browser so very little code must be trusted. XWiki Cryptographic Module provides a function for interpreting the response from the <keygen> tag and converting it to a certificate which is sent back to the user. The certificate can also be stored for later comparison to a certificate derived from a valid signature.

In order for the crypto.signText function to work properly, the user's certificate must have a signature on it which the user's browser trusts. XWiki Cryptographic Module generates a certificate authority each time a new user certificate is needed, so the user can install that authority to indicate that their trust. The authority private key is thrown away immediately after use to minimize the associated risk.

To help the user generate a client certificate and install a matching authority certificate 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}}
After installing the certificate the user will have to download the automatically added attachment "authority.crt" and add it to their browser. Firefox will add it automatically when the user clicks on it.

Importing client certificates from PEM encoded text

In an environment where client certificates are stored on smart cards or are otherwise already available, you may want to simply load the public certificate to be able to verify text signed by the user. You can import standard PEM encoded certificates as well.

How to use $services.crypto.x509.certFromPEM():

{{velocity}}
#if("$!request.getParameter('cert')" != '')
  $services.crypto.x509.certFromPEM($request.getParameter('cert'))
#else
  Paste certificate in PEM format and click "Upload Certificate".

  {{html}}
  <form id="keyForm" action='$doc.getURL()' method='post'>
   <textarea cols=50 rows=10 name="cert"></textarea>
   <br/>
   <input type="submit" value="Upload Certificate" />
  </form>
  {{/html}}
#end
{{/velocity}}
You can use $cert.toPEMString() to make a certificate into a PEM formatted string then $services.crypto.x509.certFromPEM() to get it back which is an excellent way to store certificates.

Further reading

There are more features of the XWiki Cryptographic Module which are not covered in this document.

The API Javadoc

A good way to find out what the XWiki Cryptographic Module has to offer is to look at the API Javadoc.
http://maven.xwiki.org/site/xwiki-core-parent/xwiki-core-crypto/apidocs/index.html 
Some API documents which may be of particular interest include:

Password based cryptography service, available in velocity as $services.crypto.passwd
http://maven.xwiki.org/site/xwiki-core-parent/xwiki-core-crypto/apidocs/org/xwiki/crypto/passwd/PasswordCryptoService.html
x509/CMS Cryptography service, available in velocity as $services.crypto.x509
http://maven.xwiki.org/site/xwiki-core-parent/xwiki-core-crypto/apidocs/org/xwiki/crypto/x509/X509CryptoService.html
XWiki x509 Certificate, an extension of X509Certificate with added features.
http://maven.xwiki.org/site/xwiki-core-parent/xwiki-core-crypto/apidocs/org/xwiki/crypto/x509/XWikiX509Certificate.html

The Code

If you are doing a security audit of your software or you just really want to understand what's going on inside of the XWiki Cryptographic Module, we invite you to review the source code of the module. You can find the current source on the subversion repository.
http://www.github.com/xwiki/xwiki-platform/tree/master/xwiki-platform-core/xwiki-platform-crypto
Certain items which may be of perticular interest include:

Default key derivation function for protection of passwords and encryption:
http://www.github.com/xwiki/xwiki-platform/tree/master/xwiki-platform-core/xwiki-platform-crypto/src/main/java/org/xwiki/crypto/passwd/internal/ScryptMemoryHardKeyDerivationFunction.java
Abstract password based encryption engine.
http://www.github.com/xwiki/xwiki-platform/tree/master/xwiki-platform-core/xwiki-platform-crypto/src/main/java/org/xwiki/crypto/passwd/internal/AbstractPasswordCiphertext.java
XWiki extension of X509Certificate:
http://www.github.com/xwiki/xwiki-platform/tree/master/xwiki-platform-core/xwiki-platform-crypto/src/main/java/org/xwiki/crypto/x509/XWikiX509Certificate.java
X509Certificate generator:
http://www.github.com/xwiki/xwiki-platform/tree/master/xwiki-platform-core/xwiki-platform-crypto/src/main/java/org/xwiki/crypto/x509/internal/X509Keymaker.java
PKCS#7 Signature Validator
http://www.github.com/xwiki/xwiki-platform/tree/master/xwiki-platform-core/xwiki-platform-crypto/src/main/java/org/xwiki/crypto/x509/internal/X509SignatureService.java
Tags:
Created by Vincent Massol on 2010/12/13 17:49
    

Get Connected