Developing the PKI MIDlet

This example provides the source code to demonstrate how to digitally sign data.

Note: This tutorial provides the commented source code for the PKI MIDlet example. For the source code of the example, see the Source codes for examples section of the library.

Note: This example only shows how to digitally sign data, not to receive or authenticate these signatures.

To create the class:

  1. Create the class PKIMIDLET.

  2. Import the required classes.

    /* Copyright © 2006 Nokia. */
    package com.nokia.midp.examples.security.pki;
    
    // SATSA-PKI
    import javax.microedition.pki.*;
    import javax.microedition.securityservice.*;
    
    // CRYPTO
    import java.security.*;
    import java.security.spec.*;
    import javax.crypto.*;
    import javax.crypto.spec.*;
    
    // OTHER
    import javax.microedition.midlet.*;
    import javax.microedition.lcdui.*;
    import java.io.*;
    import javax.microedition.io.*;
    
    
  3. Set the class to extend MIDlet and implement CommandListener. Set the required variables and constants. Set the variables for signed data, to be used with the authenticate and sign methods later. Set also the constants for user prompts and commands.

    /**
     * This midlet is for generating signatures using CMSMessageSignatureService-class.
     * It demostrates how to
     * Verification for these midlet-made signatures is not done in this same midlet,
     * since CMSMessageSignatureService class-created Signatures are of CMS-format
     * and Signature-class verify()-method doesn´t accept CMS-formatted signatures.
     */
    
    public class PKIMIDLET extends MIDlet implements CommandListener {
      private Form form;
      private Command exitCommand;
      private Command clearCommand;
      private Command authByteOpaqueCommand;
      private Command authByteDetachedCommand;
      private Command authStringOpaqueCommand;
      private Command authStringDetachedCommand;
      private Command signOpaqueCmd;
      private Command signDetachedCmd;
    
      // signed data
      // authenticate():
      private byte[] authByteOpaque = null;
      private byte[] authByteDetached = null;
      private byte[] authStringOpaque = null;
      private byte[] authStringDetached = null;
      // sign():
      private byte[] signOpaque = null;
      private byte[] signDetached = null;
    
      private TextField toBeSigned;
      private String stringToSign = "sign me";
      //The constants
      private static final String AUTH_SIGN_BYTE_CONTENT = "AuthenSignByteHasContent";
      private static final String AUTH_SIGN_BYTE_NO_CONTENT = "AuthenSignByteNoContent";
      private static final String AUTH_SIGN_STRING_CONTENT = "AuthenSignStringHasContent";
      private static final String AUTH_SIGN_STRING_NO_CONTENT = "AuthenSignStringNoContent";
      private static final String CMS_CONTENT = "CMS_WithContent";
      private static final String CMS_NO_CONTENT = "CMS_NoContent";
      private static final String CLEAR = "Clear";
      private static final String EXIT = "Exit";
      private static final String SIGN_LABEL = "Sign";
      private static final String SIGN_CONTENT = "ToBeSigned";
      private static final String USER_PROMPT = "Going to sign.";
      private static final String AUTH_SIGN_MSG = "Sign for authentification purpose.";
      private static final String AUTH_SIGN_MSG_BYTE_WITH_CONTENT = "Going to sign bytes with content for: ";
      private static final String AUTH_SIGN_MSG_BYTE_WITHOUT_CONTENT = "Going to sign bytes without content for: ";
      private static final String AUTH_SIGN_MSG_STRING_WITH_CONTENT = "Going to sign string with content for:  ";
      private static final String AUTH_SIGN_MSG_STRING_WITHOUT_CONTENT = "Going to sign string without content for:  ";
      private static final String CMS_SIGN_MSG = "Generate a CMS signed message.";
      private static final String CMS_SIGN_MSG_WITH_CONTENT = "Going to sign with content for ";
      private static final String CMS_SIGN_MSG_WITHOUT_CONTENT = "Going to sign without content for ";
      private static final String AFTER_SIGN_MSG = "Already signed. The length of the signed message for the above ";
      private static final String AFTER_BYTE_WITH_CONTENT = "bytes with content is: ";
      private static final String AFTER_BYTE_WITHOUT_CONTENT = "bytes without content is: ";
      private static final String AFTER_STRING_WITH_CONTENT = "string with content is: ";
      private static final String AFTER_STRING_WITHOUT_CONTENT = "string without content is: ";
      private static final String AFTER_STRING =  "string is: ";
      private static final String CANCEL = "Cancelled.";
      private static final String EXCEPTION = "Exception: ";
      private static final int AUTH_LENGTH = 128;
      private static final int SIGN_FILED_LENGTH = 100;
    
    
    
  4. Create a new instance of the Form class. Implement the OK, Clear, and Exit commands for the user interface. Implement the text field for the user interface and set a default text to be "ToBeSigned".

      public PKIMIDLET() {
        try {
          form = new Form("PKIMIDLET");
          authByteOpaqueCommand = new Command(AUTH_SIGN_BYTE_CONTENT, Command.OK, 1);
          authByteDetachedCommand = new Command(AUTH_SIGN_BYTE_NO_CONTENT, Command.OK, 2);
          authStringOpaqueCommand = new Command(AUTH_SIGN_STRING_CONTENT, Command.OK, 3);
          authStringDetachedCommand = new Command(AUTH_SIGN_STRING_NO_CONTENT, Command.OK, 4);
          signOpaqueCmd = new Command(CMS_CONTENT, Command.OK, 5);
          signDetachedCmd = new Command(CMS_NO_CONTENT, Command.OK, 6);
          clearCommand = new Command(CLEAR, Command.OK, 7);
          exitCommand = new Command(EXIT, Command.EXIT, 8);
          form.addCommand(authByteOpaqueCommand);
          form.addCommand(authByteDetachedCommand);
          form.addCommand(authStringOpaqueCommand);
          form.addCommand(authStringDetachedCommand);
          form.addCommand(signOpaqueCmd);
          form.addCommand(signDetachedCmd);
          form.addCommand(clearCommand);
          form.addCommand(exitCommand);
          form.setCommandListener(this);
    
          toBeSigned = new TextField(SIGN_LABEL, SIGN_CONTENT, SIGN_FILED_LENGTH, TextField.ANY);
          form.append(toBeSigned);
        }
        catch (Exception ex) {
          String cName = ex.getClass().getName();
          form.append(EXCEPTION + cName + ": " + ex.getMessage());
        }
      }
    
  5. Implement the startApp() method to handle the functionality of the class. This method also includes the signature creation for which details are implemented in the followings steps.

      public void startApp() {
        Display.getDisplay(this).setCurrent(form);
      }
    
      public void destroyApp(boolean b) {}
      public void pauseApp() {}
    
      public void commandAction (Command c, Displayable d) {
        if (c == exitCommand) {
          destroyApp(false);
          notifyDestroyed();
        }
    
    
  6. Generate a der-encoded signature including byte array content (opaque) for authentication purposes.

    The authenticate method generates the signature according to the set parameters. byteDataToSign refers to the byte array that is to be signed. CMSMessageSignatureService.SIG_INCLUDE_CERTIFICATE and CMSMessageSignatureService.SIG_INCLUDE_CONTENT are the signature options meaning that both certificate and content are included in the signature. caNames (an array of strings) contains the names of trusted certification authorities. userPrompt tells the user that the application is going to sign the data.

    The phrase String[] caNames = null; sets the parameter caNames to null. This allows that when the parameter is used with the authenticate method, the implementation of the API automatically searches for certificates on the device and handles the interaction with the user for choosing the correct certificate. Certificates are looked for on the device and on the smart card. In practise, the keys must reside on the smart card, but the certificate may reside on the device. Note that the system clock is used for determining the validity of certificates.

    CMSMessageSignatureServiceException is used to handle situations when the errors occur, the requested certificate is not available, or opaque signatures are not allowed.

    UserCredentialManagerException is used to indicate a situation when a security element cannot be found.

        // getting der-encoded signature incl. byte array-content (opaque)
        // FOR AUTHENTICATION PURPOSES
        else if (c == authByteOpaqueCommand) {
          try {
            authByteOpaque = new byte[AUTH_LENGTH];
            String[] caNames = null; // implementation provides search
            byte[] byteDataToSign = stringToSign.getBytes();
            String userPrompt = USER_PROMPT;
            form.append(AUTH_SIGN_MSG);
            form.append(AUTH_SIGN_MSG_BYTE_WITH_CONTENT  + "\"" + stringToSign + ".\"");
            authByteOpaque = CMSMessageSignatureService.authenticate(
                byteDataToSign,
                CMSMessageSignatureService.SIG_INCLUDE_CERTIFICATE
                |CMSMessageSignatureService.SIG_INCLUDE_CONTENT,
                caNames, userPrompt);
    
            if(authByteOpaque != null) {
              form.append(AFTER_SIGN_MSG);
              form.append(AFTER_BYTE_WITH_CONTENT + authByteOpaque.length);
            }
            else {
              form.append(CANCEL);
            }
          }
          catch (SecurityException se){
            form.append("Too many incorrect pin entries.");
          }
          catch (CMSMessageSignatureServiceException cmsEx){
            form.append("Error occured during signature generation.");
            if(cmsEx.getReason()==CMSMessageSignatureServiceException.CRYPTO_NO_CERTIFICATE){
              form.append("The certificate is not available.");
            }
            if(cmsEx.getReason()==CMSMessageSignatureServiceException.CRYPTO_NO_OPAQUE_SIG){
              form.append("Opaque signature is not supported.");
            }
          }
          catch (UserCredentialManagerException ucmEx){
            if(ucmEx.getReason()== UserCredentialManagerException.SE_NOT_FOUND){
              form.append("A security element is not found.");
            }
          }
          catch (Exception ex) {
            String cName = ex.getClass().getName();
            form.append(EXCEPTION + cName + ": " + ex.getMessage());
          }
        }
    
  7. Generate a der-encoded signature without byte array content (detached).

    The authenticate method generates the signature according to the set parameters. byteDataToSign refers to the byte array that is to be signed. CMSMessageSignatureService.SIG_INCLUDE_CERTIFICATE is the signature option meaning that only the certificate and no content is included in the signature. caNames (an array of strings) contains the names of trusted certification authorities. userPrompt tells the user that the application is going to sign the data.

    The phrase String[] caNames = null; sets the parameter caNames to null. This allows that when the parameter is used with the authenticate method, the implementation of the API automatically searches for certificates on the device and handles the interaction with the user for choosing the correct certificate. Note that the system clock is used for determining the validity of certificates.

    CMSMessageSignatureServiceException is used to handle situations when errors occur, the requested certificate is not available, or detached signatures are not supported.

    UserCredentialManagerException is used to indicate a situation when a security element cannot be found.

        // getting der-encoded signature without byte array-content (detached)
        // FOR AUTHENTICATION PURPOSES
        else if (c == authByteDetachedCommand) {
          try {
            authByteDetached = null;
            byte[] byteDataToSign = stringToSign.getBytes();
            String[] caNames = null;
            String userPrompt = USER_PROMPT;
            form.append(AUTH_SIGN_MSG);
            form.append(AUTH_SIGN_MSG_BYTE_WITHOUT_CONTENT + "\"" + stringToSign + ".\"");
            authByteDetached = CMSMessageSignatureService.authenticate(
                byteDataToSign,
                CMSMessageSignatureService.SIG_INCLUDE_CERTIFICATE,
                caNames, userPrompt);
            if(authByteDetached != null) {
              form.append(AFTER_SIGN_MSG);
              form.append(AFTER_BYTE_WITHOUT_CONTENT + authByteDetached.length);
            }
            else {
              form.append(CANCEL);
            }
          }
          catch (SecurityException se){
            form.append("Too many incorrect pin entries.");
          }
          catch (CMSMessageSignatureServiceException cmsEx){
            form.append("Error occured during signature generation.");
            if(cmsEx.getReason()== CMSMessageSignatureServiceException.CRYPTO_NO_CERTIFICATE){
              form.append("The certificate is not available.");
            }
            if(cmsEx.getReason()== CMSMessageSignatureServiceException.CRYPTO_NO_DETACHED_SIG){
              form.append("Detached sigatures are not supported.");
            }
    
          }
          catch (UserCredentialManagerException ucmEx){
            if(ucmEx.getReason()== UserCredentialManagerException.SE_NOT_FOUND){
              form.append("A security element is not found.");
            }
          }
          catch (Exception ex) {
            String cName = ex.getClass().getName();
            form.append(EXCEPTION + cName + ": " + ex.getMessage());
          }
        }
    
  8. Generate a der-encoded signature including string content (opaque).

    The authenticate method generates the signature according to the set parameters. stringToSign refers to the string that is to be signed. CMSMessageSignatureService.SIG_INCLUDE_CERTIFICATE and CMSMessageSignatureService.SIG_INCLUDE_CONTENT are the signature options meaning that both certificate and content are included in the signature. caNames (an array of strings) contains the names of trusted certification authorities. userPrompt tells the user that the application is going to sign the data.

    The phrase String[] caNames = null; sets the parameter caNames to null. This allows that when the parameter is used with the authenticate method, the implementation of the API automatically searches for certificates on the device and handles the interaction with the user for choosing the correct certificate. Note that the system clock is used for determining the validity of certificates.

    CMSMessageSignatureServiceException is used to handle situations when errors occur, the requested certificate is not available, or opaque signatures are not supported.

    UserCredentialManagerException is used to indicate a situation when a security element cannot be found.

        // getting der-encoded signature incl. String content (opaque)
        // FOR AUTHENTICATION PURPOSES
        else if (c == authStringOpaqueCommand) {
          try {
            authStringOpaque = null;
            String[] caNames = null;
            String userPrompt = USER_PROMPT;
            form.append(AUTH_SIGN_MSG);
            form.append(AUTH_SIGN_MSG_STRING_WITH_CONTENT + "\"" + stringToSign + ".\"");
            authStringOpaque = CMSMessageSignatureService.authenticate(
                stringToSign,
                CMSMessageSignatureService.SIG_INCLUDE_CERTIFICATE
                |CMSMessageSignatureService.SIG_INCLUDE_CONTENT,
                caNames, userPrompt);
    
            if(authStringOpaque != null) {
              form.append(AFTER_SIGN_MSG);
              form.append(AFTER_STRING_WITH_CONTENT + authStringOpaque.length);
            }
            else {
              form.append(CANCEL);
            }
          }
          catch (SecurityException se){
            form.append("Too many incorrect pin entries.");
          }
          catch (CMSMessageSignatureServiceException cmsEx){
            form.append("Error occured during signature generation.");
            if(cmsEx.getReason()== CMSMessageSignatureServiceException.CRYPTO_NO_CERTIFICATE){
              form.append("The certificate is not available.");
            }
            if(cmsEx.getReason()== CMSMessageSignatureServiceException.CRYPTO_NO_OPAQUE_SIG){
              form.append("Opaque signature is not supported.");
            }
          }
          catch (UserCredentialManagerException ucmEx){
            if(ucmEx.getReason()== UserCredentialManagerException.SE_NOT_FOUND){
              form.append("A security element is not found.");
            }
          }
    
          catch (Exception ex) {
            String cName = ex.getClass().getName();
            form.append(EXCEPTION + cName + ": " + ex.getMessage());
          }
        }
    
  9. Generate a der-encoded signature without string content (detached).

    The authenticate method generates the signature according to the set parameters. stringToSign refers to the byte array that is to be signed. CMSMessageSignatureService.SIG_INCLUDE_CERTIFICATE is the signature option meaning that only a certificate and no content is included in the signature. caNames (an array of strings) contains the names of trusted certification authorities. userPrompt tells the user that the application is going to sign the data.

    The phrase String[] caNames = null; sets the parameter caNames to null. This allows that when the parameter is used with the authenticate method, the implementation of the API automatically searches for certificates on the device and handles the interaction with the user for choosing the correct certificate. Note that the system clock is used for determining the validity of certificates.

    CMSMessageSignatureServiceException is used to handle situations when errors occur, the requested certificate is not available, or detached signatures are not supported.

    UserCredentialManagerException is used to indicate a situation when a security element cannot be found.

        // getting der-encoded signature without String content (detached)
        // FOR AUTHENTICATION PURPOSES
        else if (c == authStringDetachedCommand) {
          try {
            authStringDetached = null;
            String[] caNames = null;
            String userPrompt = USER_PROMPT;
            form.append(AUTH_SIGN_MSG);
            form.append(AUTH_SIGN_MSG_STRING_WITHOUT_CONTENT + "\"" + stringToSign + ".\"");
            authStringDetached = CMSMessageSignatureService.authenticate(
                stringToSign,
                CMSMessageSignatureService.SIG_INCLUDE_CERTIFICATE,
                caNames, userPrompt);
    
            if(authStringDetached != null) {
              form.append(AFTER_SIGN_MSG);
              form.append(AFTER_STRING_WITHOUT_CONTENT + authStringDetached.length);
            }
            else {
              form.append(CANCEL);
            }
          }
          catch (SecurityException se){
            form.append("Too many incorrect pin entries.");
          }
          catch (CMSMessageSignatureServiceException cmsEx){
            form.append("Error occured during signature generation.");
            if(cmsEx.getReason()== CMSMessageSignatureServiceException.CRYPTO_NO_CERTIFICATE){
              form.append("The certificate is not available.");
            }
            if(cmsEx.getReason()== CMSMessageSignatureServiceException.CRYPTO_NO_DETACHED_SIG){
              form.append("Detached sigatures are not supported.");
            }
    
          }
          catch (UserCredentialManagerException ucmEx){
            if(ucmEx.getReason()== UserCredentialManagerException.SE_NOT_FOUND){
              form.append("A security element is not found.");
            }
          }
          catch (Exception ex) {
            String cName = ex.getClass().getName();
            form.append(EXCEPTION + cName + ": " + ex.getMessage());
          }
        }
    
    
  10. Generate a der-encoded signature including content (opaque).

    The sign method generates a CMS signed message according to the set parameters. stringToSign refers to the string that is to be signed. CMSMessageSignatureService.SIG_INCLUDE_CERTIFICATE and CMSMessageSignatureService.SIG_INCLUDE_CONTENT are the signature options meaning that both certificate and content are included in the signature. caNames (an array of strings) contains the names of trusted certification authorities. userPrompt tells the user that the application is going to sign the data.

    The phrase String[] caNames = null; sets the parameter caNames to null. This allows that when the parameter is used with the authenticate method, the implementation of the API automatically searches for certificates on the device and handles the interaction with the user for choosing the correct certificate. Note that the system clock is used for determining the validity of certificates.

    CMSMessageSignatureServiceException is used to handle situations when errors occur, the requested certificate is not available, or opaque signatures are not supported.

    UserCredentialManagerException is used to indicate a situation when a security element cannot be found.

        // getting der-encoded signature incl. content (opaque)
        // FOR NON-REPUDIATION (authorization, signatures)
        else if (c == signOpaqueCmd) {
          try {
            signOpaque = null;
            String[] caNames = null;
            String userPrompt = USER_PROMPT;
            form.append(CMS_SIGN_MSG);
            form.append(CMS_SIGN_MSG_WITH_CONTENT + "\"" + stringToSign + ".\"");
            signOpaque = CMSMessageSignatureService.sign(
                stringToSign,
                CMSMessageSignatureService.SIG_INCLUDE_CERTIFICATE
                |CMSMessageSignatureService.SIG_INCLUDE_CONTENT,
                caNames, userPrompt);
    
            if(signOpaque != null) {
              form.append(AFTER_SIGN_MSG);
              form.append(AFTER_STRING + signOpaque.length);
            }
            else {
              form.append(CANCEL);
            }
          }
          catch (SecurityException se){
            form.append("Too many incorrect pin entries.");
          }
          catch (CMSMessageSignatureServiceException cmsEx){
            form.append("Error occured during signature generation.");
            if(cmsEx.getReason()== CMSMessageSignatureServiceException.CRYPTO_NO_CERTIFICATE){
              form.append("The certificate is not available.");
            }
            if(cmsEx.getReason()== CMSMessageSignatureServiceException.CRYPTO_NO_OPAQUE_SIG){
              form.append("Opaque signature is not supported.");
            }
          }
          catch (UserCredentialManagerException ucmEx){
            if(ucmEx.getReason()== UserCredentialManagerException.SE_NOT_FOUND){
              form.append("A security element is not found.");
            }
          }
    
          catch (Exception ex) {
            String cName = ex.getClass().getName();
            form.append(EXCEPTION + cName + ": " + ex.getMessage());
          }
        }
    
  11. Generate a der-encoded signature without content (detached).

    The sign method generates a CMS signed message according to the set parameters. stringToSign refers to the string that is to be signed. CMSMessageSignatureService.SIG_INCLUDE_CERTIFICATEis the signature option meaning that only a certificate and no content is included in the signature. caNames (an array of strings) contains the names of trusted certification authorities. userPrompt tells the user that the application is going to sign the data.

    The phrase String[] caNames = null; sets the parameter caNames to null. This allows that when the parameter is used with the authenticate method, the implementation of the API automatically searches for certificates on the device and handles the interaction with the user for choosing the correct certificate. Note that the system clock is used for determining the validity of certificates.

    CMSMessageSignatureServiceException is used to handle situations when errors occur, the requested certificate is not available, or detached signatures are not supported.

    UserCredentialManagerException is used to indicate a situation when a security element cannot be found.

        // getting der-encoded signature without content (detached)
        // FOR NON-REPUDIATION (authorization, signatures)
        else if (c == signDetachedCmd) {
          try {
            signDetached = null;
            String[] caNames = null;
            String userPrompt = USER_PROMPT;
            form.append(CMS_SIGN_MSG);
            form.append(CMS_SIGN_MSG_WITHOUT_CONTENT + "\"" + stringToSign + ".\"");
            signDetached = CMSMessageSignatureService.sign(
                stringToSign,
                CMSMessageSignatureService.SIG_INCLUDE_CERTIFICATE,
                caNames, userPrompt);
    
            if(signDetached != null) {
              form.append(AFTER_SIGN_MSG);
              form.append(AFTER_STRING + signDetached.length);
            }
            else {
              form.append(CANCEL);
            }
          }
          catch (SecurityException se){
            form.append("Too many incorrect pin entries.");
          }
          catch (CMSMessageSignatureServiceException cmsEx){
            form.append("Error occured during signature generation.");
            if(cmsEx.getReason()== CMSMessageSignatureServiceException.CRYPTO_NO_CERTIFICATE){
              form.append("The certificate is not available.");
            }
            if(cmsEx.getReason()== CMSMessageSignatureServiceException.CRYPTO_NO_DETACHED_SIG){
              form.append("Detached sigatures are not supported.");
            }
    
          }
          catch (UserCredentialManagerException ucmEx){
            if(ucmEx.getReason()== UserCredentialManagerException.SE_NOT_FOUND){
              form.append("A security element is not found.");
            }
          }
          catch (Exception ex) {
            String cName = ex.getClass().getName();
            form.append(EXCEPTION + cName + ": " + ex.getMessage());
          }
        }
    
        else if (c == clearCommand) {
          form.deleteAll();
          toBeSigned = new TextField(SIGN_LABEL, SIGN_CONTENT, SIGN_FILED_LENGTH, TextField.ANY);
          form.append(toBeSigned);
        }
      }
    }