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:
Create the
class PKIMIDLET
.
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.*;
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;
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()); } }
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(); }
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()); } }
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()); } }
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()); } }
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()); } }
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()); } }
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_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 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); } } }