|
By C. Enrique Ortiz, September 2005
|
|
|
Introduced with the Java Specification Request 177, SATSA defines a set of APIs that allows J2ME applications to communicate with and access functionality, secure storage and cryptographic operations provided by security elements such as smart cards and Wireless Identification Modules (WIM).
Part 1 of this article provided a general overview of SATSA, covered the communication APIs, and introduced the SATSA reference implementation. In this second part of this article I will cover the SATSA security APIs, including terminologies, typical use-cases, and code examples.
SATSA makes a good job of simplifying complexity, by providing an easy to use API. Yet, the topic of security is a complex one and this article covers a lot of background information. The goal of this article is to introduce you to the main concepts of PKI and cryptography with respect to SATSA. Writing secure applications is nothing trivial and anyone writing secure applications must take the time to understand the underpinnings of PKI and cryptography in general; you can find a list of resources at the end of this article.
Contents
The SATSA security APIs are in part a subset of the J2SE security and cryptographic APIs, with a lot of influence from the Java Card security API. The SATSA security APIs consist of two separate but related J2ME optional packages:
- The Public Key Infrastructure (PKI) optional package.
- The cryptographic (CRYPTO) optional package.
These APIs enable J2ME applications to:
- Work with public digital certificates, public and private keys, message digests and digital signatures.
- Create, store, and use user-credentials based on X. 509 digital certificates.
- Encrypt data using asymmetric and symmetric cryptography.
The next two tables summarize the SATSA Java packages. The first table summarizes the SATSA Public Key Infrastructure packages that contain the APIs for digital certificates and signatures:
Table 1: SATSA PKI Security Packages
 |
javax.microedition.pki
|
Classes to support basic user-certificate management.
This PKI optional package is different than the corresponding J2SE PKI package.
Defines the class UserCredentialManager that provides methods to request a Certificate Signing Request (CSR), and to add and remove certificates from the certificate store. It also defines the exception class UserCredentialManagerException that is thrown if an error is encountered when handling certificates. The initial version of SATSA supports X.509 version 3 certificates.
In MIDP 2.0, this package also contains the interface Certificate, which provides a common interface to (X.509) certificates, and the exception class CertificateException that is thrown when an error is encountered while using a Certificate.
|
javax.microedition.securityservice
|
Classes to generate application-level digital signatures that conform to the Cryptographic Message Syntax (CMS) format.
Defines the class CMSMessageSignatureService that provides methods to generate digital signatures based on certificates. It also defines the exception class CMSMessageSignatureServiceException that is thrown if an error is encountered when handling signatures.
|
The next table summarizes the SATSA CRYPTO packages that contain the APIs for public/private keys, signature verification, message digests, and data encryption:
Table 2: SATSA CRYPTO Security Packages
 |
java.security
|
Classes and interfaces for the security framework.
A subset of J2SE java.security package.
Defines the interfaces Key and PublicKey and the classes KeyFactory, MessageDigest, and Signature that provide basic support for public keys, message digests, and digital signatures.
|
java.security.spec
|
Classes and interfaces for key specifications and algorithm parameter specifications.
A subset of J2SE java.security.spec package.
Defines the interfaces AlgorithmParameterSpec and KeySpec, and the classes EncodedKeySpec and X509EncodedKeySpec for key and algorithm parameter specifications.
|
javax.crypto
|
Classes and interfaces for cryptographic operations.
A subset of J2SE javax.crypto package.
Defines the class Cipher that provides basic support for encryption and decryption of data, and various cryptographic exceptions.
|
javax.crypto.spec
|
Classes and interfaces for key specifications.
A subset of J2SE javax.crypto.spec package.
Defines the classes IvParameterSpec and SecretKeySpec for key and algorithm parameter specifications.
|
In addition, there are a number of exceptions not mentioned in the above tables; please see the SATSA specification for a complete list of exceptions.
PKI consists of a set of technologies for authentication, data confidentiality and data integrity. But PKI is more than just a set of cryptographic technologies. It also consists of management services for the creation, distribution, maintenance and validation of digital certificates, and of policies and procedures such as the Certification Practices Statement (CPS) that documents the operational practices and guidelines that Certificate Authorities must follow.
The National Institute of Standards and Technology (NIST) defines cryptography as "the science of mapping readable text, called plaintext, into an unreadable format, called ciphertext, and vice versa. The mapping process is a sequence of mathematical computations. The computations affect the appearance of the data, without changing its meaning."
Cryptography in general has four main goals, all addressed with SATSA:
- Confidentiality or ensuring that only authorized recipients can access information. This is accomplished by using data encryption.
- Data integrity or the ability to detect if information has changed. This is accomplished by using digital signatures.
- Non-repudiation or the ability to ensure that a transaction can't be denied. This is accomplished by using non-repudiation type of signatures.
- Authentication or the ability to verify the source of information. This accomplished by using data encryption and digital signatures.
Cryptography in general is based on the use of keys for the transformation of plaintext to ciphertext and back. There are two cryptography models: symmetric and asymmetric. Symmetric cryptography uses a single secret key, while asymmetric uses two keys, a private and a public key pair. Each cryptography model has pros and cons, for example, in the areas of performance and key distribution. From the performance perspective, symmetric cryptography is more efficient (computationally faster) than its asymmetric counterpart, but from the key distribution perspective (how keys are shared), asymmetric cryptography provides a more convenient and safer model because the public keys can be shared as needed without fear of compromising security (since the private key can be kept secret). This is contrary to symmetric cryptography where special care must be given (secret key must be kept and distributed secretly) to ensure security is not compromised. Because of these reasons, symmetric cryptography is best suited for data encryption, while asymmetric cryptography is best suited for authentication, non-repudiation and data-integrity through the use of digital signatures. I will cover more about these implications in the data encryption section. As you will see, SATSA security APIs encourages asymmetric cryptography for digital signatures, authentication and non-repudiation, while encryption SATSA is mainly targeted at symmetric cryptography.
There are many functional elements and interactions in PKI, as Figure 1 summarizes:
 |
|
Figure 1: PKI Functional Elements
|
These are summarized next:
End-entity - a generic term that describes the end-users consumers of PKI services. This could be a person, or a computer. In SATSA the J2ME handset (application) is an end-entity. To get a digital certificate, end-entities go through an enrollment process.
Certificate Authority (CA) - a trusted 3rd party responsible for the management of digital certificates. Certificate Authorities are at the center of the PKI trust model. A CA is responsible for issuing signed digital certificates, for keeping a certificate repository (2a), and of managing revoked certificates and the associated the Certification Revocation List (CRL) repository (2b). These repositories typically are LDAP based.
Certificate Signing Request (CSR) - a document generated by an end-entity for certificate enrollment. A CSR contains information about the user such as its distinguished name, a public key (signature), and other information. A CSR is encoded using the Distinguished Encoding Rules for ASN.1 as defined in PKCS #10: Certification Request Syntax Version. Once the CSR has been verified by a CA, the CA generates a signed certificate.
Public Digital Certificate and Certificate Path - a digital certificate, also referred to as public key certificate, is the public component in PKI. A public certificate represents the credentials for a given end-entity by binding a specific user to a public key. The end-entity represented by a certificate holds the private key that corresponds to that certificate. Certificates are primarily used for digital signatures to verify the origin (authentication), and integrity of information, and can also be used for non-repudiation. X.509 Version 3 is the most predominant format for digital certificates. An X.509 certificate contains all the information about an entity including authentication/verification information, for example:
- The CA (issuer) serial number.
- A signature algorithm used to sign the certificate.
- The distinguished name of the certificate issuer.
- Validity period for the certificate.
- The subject's distinguished name - the subject could be the root CA, of intermediate CAs or the end-entity depending on the certificate's role on a certificate path; certificate path is explained shortly.
- Subject's public key.
- The issuer's unique ID.
- Extension fields for constraints such as key usage restrictions and certificate policies.
- The certificate's signature of all the above fields.
Certificates are requested by an end-entity and issued by a CA. These public key certificates can be shared by its holder as needed. When working with certificates you will encounter the term certificate path or PkiPath. A PkiPath is a (ASN.1 DER encoded) set of certificates that includes the user's certificate and CA certificates. This is illustrated next:
 |
|
Figure 2: A Certificate Path
|
A certificate path (or chain, as it is also referred) consists of one of more certificates. The sequence order is such that the first certificate is the self-signed CA certificate which is the issuer of the second certificate, which in turn is the issuer of the third, and so on. The chain ends with the end-entity (user) certificate. In the chain, each certificate must be unique, and cannot repeat. Before an end-entity certificate is used, each certificate in its chain is typically verified by verifying signatures, validity period and if revoked.
A Certificate Revocation List (CRL) - a list of revoked certificates. During certificate validation a certificate holder checks with the appropriate CA's CRL to verify the revocation status for a given certificate. An alternative to CRLs is the Online Certificate Status Protocol (OCSP) that can be used to retrieve revocation and additional status information for a given certificate.
Life-cycle and Usage of Digital Certificates
The PKI model is based on trust; trust that certificates are issued by a valid issuer (CA), trust that a certificate really belongs to the person indicated in the certificate, trust that private keys are properly safeguarded, and trust that invalid certificates have been properly invalidated or revoked.
At any given time a digital certificate is either issued-and-valid, or revoked. It all begins with a certificate being issued by a trusted CA in response to a CSR . this is the enrollment process on which the CA verifies the identity of the requestor, and issues a signed certificate. And it all ends when a CA revokes a certificate, by including it in the CRL or responding to Online Certificate Status Protocol (OCSP) requests, typically because the certificate.s validity period has expired or the certificate is no longer is valid.
When not revoked, a certificate is distributed, verified for validity, and primarily used for authentication, non-repudiation, and data integrity; during this period, the private key associated to the public key certificate must be safeguarded by its owners.
Key usage begins with key distribution. Because a certificate represents a single end-entity (person or computer), you must have the public keys for every end-entity with whom you must have a secure conversation; same with sharing keys . you must distribute your public key with every person with you must have a secure conversation.
Once public keys are properly distributed, an end-entity.s public key is used to verify their digital signatures. That same public key can be used to encrypt data that can only be decrypted by the corresponding private key. Note that as previously mentioned, symmetric encryption is best suited for encryption instead.
In this section I will cover some typical use-cases and how SATSA addresses them, including the certificate enrollment process, managing digital certificates, using message digests, signatures, and how to encrypt information.
Practical Considerations
When it is time to write your SATSA-based application, there are some practical considerations to be aware of:
Public key distribution, the act of sharing public keys, is outside the SATSA API.s scope. The API though supports the creation of signed content that includes a public key certificate; this allows you to share signed content, and the public key that correspond to the private key used to sign such content.
SATSA provides a certificate store for certificates. But it doesn.t provide a mechanism to store 3rd party public key certificates that you might need for encryption. You must store these using the underlying store, for example in MIDP the Record Management System (RMS).
ASN.1 and related encoding rules such as Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER) are used to encode information such as public keys, signatures, and cryptographic messages. Most of time, encoding and decoding of messages are handled by the API, for example when generating formatted digital signatures. In other cases, such as when extracting information from digital certificates, you either must implement this functionality in your application, or provide/consume these as server services.
As in the Java Card API, SATSA.s Cipher class doesn.t provide the method getOutputSize() to help determine buffer sizes for allocation purposes. You must understand the algorithms in use and implement helper methods to help determine this.
PKI and cryptographic operations such as generating a CSR, sending the CSR to a CA, generating digital signatures or encrypting data typically are all long, time-consuming operations that should be dispatched in their own thread of execution to prevent system and UI threads from freezing.
PKI operations such as generating a CSR and managing the certificate local store are not really intended for typically application usage, and instead are intended for a certificate management client application that is likely provided by a CA and that knows how to coordinate with server side functions.
Attempting to access the trusted certificate local store results in SATSA prompting the user for permission, by displaying the certificate.s name and/or URL, and even maybe prompting for a valid Personal Identification Number (PIN). How this prompt is presented to the user is implementation specific.
SATSA follows the security/permission policy of the underlying runtime environment. For example in MIDP 2.0, it must follow the permission model that is based on security policies and entries made into the JAD or manifest files.
To use the SATSA API, your application must be part of the right permission domain; for example in MIDP 2.0 the application must be part of the operator, manufacturer or third-party trusted domain.
Certificate Enrollment
Before a digital certificate can be used, it must first be issued by a trusted Certificate Authority following what is referred to as a certificate enrollment process. A PKI certificate enrollment is a multi-step process, as summarized next:
 |
|
Figure 3: Public Key Certificate Enrollment Process
|
In this process, the following steps are performed:
A distinguished name is defined. A distinguished name (DN) is a set of attribute values that uniquely identifies an end-entity (which is stored as an object within a directory information tree). In the case of PKI and X.509 certificates, a DN is based on the Lightweight Directory Access Protocol (v3): UTF-8 String Representation of Distinguished Names (RFC 2253), and is used to identify a certificate's Subject or Issuer; the subject is the end-entity who the certificate belongs to, and the issuer is the CA that issued the certificate. The following table shows the X.500 attributes used in X.509 certificates:
Table 1: LDAP v3 String Representation of Distinguished Names
 |
CN |
commonName
|
L |
localityName
|
ST |
stateOrProvinceName
|
O |
organizationName
|
OU |
organizationalUnitName
|
C |
countryName
|
STREET |
streetAddress
|
DC |
domainComponent
|
UID |
userid
|
For example:
"CN=eortiz@j2medeveloper.com,O=J2MEDeveloper.com,UID=eortiz,C=USA"
A private key is generated based on a specified algorithm and key-length, and is stored in a certificate store that resides on a trusted security element, such as a smart-card or maybe locally on the J2ME device.
Once the DN and private key have been defined, a Certificate Signing Request is generated. The CSR contains information about the user, including the user's DN, public key, and other information.
The generated CSR is sent for verification to an enrollment computer belonging to a trusted Certificate Authority, possibly over a secure connection. There are a number of trusted CAs such as Verisign, Entrust, and Thawte; even institutions such as banks or large corporations may decide to run their own CA software. The CA you would use will depend on your application and customers - for example, for a banking application the bank institution may already have a relationship with a given set of CAs. Typically there will be a root certificate already on your handset that corresponds to trusted CAs.
Once the CA has validated the CSR, a signed public key X.509 certificate (path) is returned.
The returned X.509 certificate's chain and/or URI are stored in the local certificate store.
The following sections describe the above steps in more detail.
Generating the Private Key and CSR
SATSA simplifies the process of generating a private key and CSR. The SATSA certificate management API can be found in the PKI optional package and the UserCredentialManager class. The SATSA PKI method UserCredentialManager.generateCSR() takes care of generating a private key (or using an existing one that resides on a specified security element) and of generating the DER encoded PKCS#10 Certificate Signing Request. The following code snippet shows how to use the generateCSR() method. The first part of the snippet defines the distinguished name, algorithm and key length to use, and other information:
Generating a Private Key and Certificate Signing Request
// Define the parameters for generating a certificate signing request.
byte[] csr = null; // Buffer for generated CSR
String distinguishedName = "CN=eortiz@j2medeveloper.com,O=J2MEDeveloper.com,UID=eortiz,C=USA"; // The DN
int rsaKeyLength = 1024;
String securityElementID = null; // Use default security element
String securityElementPrompt = null; // No prompt
boolean forceKeyGen = true; // Generate private key
try {
// Generate a private key and a CSR
csr = UserCredentialManager.generateCSR(
distinguishedName,
UserCredentialManager.ALGORITHM_RSA,
rsaKeyLength,
UserCredentialManager.KEY_USAGE_AUTHENTICATION,
securityElementID,
securityElementPrompt,
forceKeyGen);
} catch (Exception e) {
// Handle IllegalArgumentException or
// UserCredentialManagerException or
// SecurityException or
// CMSMessageSignatureServiceException
...
}
|
The method blocks until the CSR has been generated or an exception is thrown. The following bullets highlight information about the above code snippet:
- SATSA currently only supports X.509 Version 3 certificates.
- Input arguments to
generateCSR() are the subject distinguished name, the public key algorithm, the key-length, the key-usage, where to store the generated private key, a user-prompt to ask where to store the private key, and if the private key should be generated or a previous one re-used.
- The above code snippet uses a distinguished name that I came up with for illustration purposes:
CN=eortiz@j2medeveloper.com,O=J2MEDeveloper.com,UID=eortiz,C=USA. Check with your CA to determine the attributes that you must supply. If DN is null, the implementation chooses an appropriate distinguished name such as a WIM serial number.
- SATSA supports various signature algorithms. The example uses the RSA but
DSA is also supported.
- When creating a key its usage or
keyUsage must be specified. As the name implies, this defines usage constraints or permissible usages for the key. WhileX.509 defines a number of key usages, SATSA only supports the generation of certificates for the purpose of authentication and non-repudiation. We will see shortly how the key usage comes into play when using the SATSA signature APIs.
- If the parameter
forceKeyGen is set to true, a private key is generated, and if false an existing private key from the specified secure element key store is used, if one found.
- The Security Element ID is a string that identifies the security element from where to get the key to include in the CSR, and if generating one, where to store it. If
null, the implementation chooses the first available one.
- Security Element Prompt is a string that contains the prompt to show the user when a secure element is not found. For example "Please insert the security element for Service ABC". If
null, the user is not prompted.
Method generateCSR() may throw various exceptions: IllegalArgumentException, UserCredentialManagerException, SecurityException, and CMSMessageSignatureServiceException. Please refer to the SATSA Javadoc and/or specification for more information.
Requesting the Signed Certificate (Verifying the CSR)
The next step in the certificate enrollment process is sending the generated CSR to the Certificate Authority's for verification. The CSR can be sent over a TCP or HTTP connection, typically using a secure connection. The method to use depends on the connectivity options offered by the CA. For this discussion let's define a method called secureSend(url, csr), a placeholder for a helper method to securely send the CSR; but I don't cover its implementation:
// Send the generated CSR to the CA enrollment server,
// possibly over a secure TCP (SecureConnection) or HTTPS
//(HttpsConnection). Wait for response (the signed X.509
// certificate chain)
String url = "www.j2medeveloper-ca.com:443";
byte[] response = secureSend(url, csr);
...
|
The CA verifies the validity of the received CSR's and if valid, a signed X.509 certificate (path) is generated and returned. This whole process may not be immediate, since the user must be verified, and the signed X.509 certificate may be returned sometime in the future, possibly over email.
Storing the Certificate
The last step in the certificate enrollment process is storing the signed X.509 certificate (path) sent by the CA into the certificate local store:
// Parse response, extracting the signed X.509
// certificate information. Store the received
// certificate on the security element.
UserCredentialManager.addCredential(...);
|
This is covered next.
A SATSA-enabled J2ME device provides a trusted certificate and key store. This store is used to save signed certificates. To manage this store, class UserCredentialManager defines two methods, one to add certificates to the store, and one to remove certificates from the store:
addCredential - to store a certificate, for example when a new signed certificate path that resulted from an enrollment process has been received from a CA.
removeCredential - to remove a certificate, for example, when a given certificate is no longer needed because it has been revoked or has expired.
The store location and its implementation details are transparent to the J2ME application.
The code snippet below shows how to use the method addCredential() to add a signed certificate path and URI extracted from enrollment response from the CA:
Storing a Certificate into the Certificate Local Store
// The certificate friendly name
String certDisplayName = new String("MyCertificate");
// The certificate path and URI.
byte[] pkiPath = "..."; // from the enrollment response
String uri = "..."; // from the enrollment response
try {
// Store the received certificate on
// the security element.The pkiPath and URI are
// extracted from the message received from the CA.
boolean added;
added = UserCredentialManager.addCredential(
certDisplayName,
pkiPath,
uri);
// Check if user cancelled the operation.
if (added == false) {
// User canceled
...
}
} catch (Exception e) {
// Handle IllegalArgumentException or
// UserCredentialManagerException or
// SecurityException
...
}
|
The following bullets highlight information about the above code snippet:
- The certificate display name is a unique friendly name that is used to easily identify a certificate at a later time.
- The variables
pkiPath and uri are assumed to be extracted from the enrollment response message containing the ASN.1 DER-encoded certificate chain and/or the certificate path's URL. How the response message is parsed is outside the scope of this article.
- The certificate or its URL or both (up to the implementation to define what to use) are then stored by the application in the certificate store using the method
addCredential().
- The method
addCredential() returns true if the credential was added and false if the user canceled the operation, and may throw the following exceptions: IllegalArgumentException, UserCredentialManagerException, or SecurityException. Please refer to the specification for more information about the exceptions.
The following code snippet shows how to use the method remoteCredential() to remove a certificate from the certificate store:
Removing a Certificate from the Certificate Local Store
// The certificate friendly name
String certDisplayName = "MyCertificate";
// DER encoded issuer DN and serial number
byte[] issuerAndSerialNumber = "...";
String securityElementID = null; // Use "default" available security element
String securityElementPrompt = null; // Don't prompt the user
try {
// Remove the named certificate from the
// security element
boolean removed;
removed = UserCredentialManager.removeCredential(
certDisplayName,
issuerAndSerialNumber,
securityElementID,
securityElementPrompt);
// Check if user cancelled the operation.
if (removed == false) {
// User canceled
...
}
} catch (Exception e) {
// Handle IllegalArgumentException or
// UserCredentialManagerException or
// SecurityException
...
}
|
The following bullets highlight information about the above code example:
- Before calling the method
removeCredential(), the certificate friendly name (or certDisplayName) and the certificate issuer and its serial number must be known ahead of time.
- The certificate display name is the unique friendly name assigned when the certificate was originally stored.
- The issuer and its serial number are DER-encoded using an ASN.1 as defined in RFC 3369 section 10.2.4 titled
IssuerAndSerialNumber. How to encode the IssuerAndSerialNumber is outside the scope of this article.
- The Security Element ID is a string that identifies the security element where the key resides. If
null, the implementation chooses the first available one.
- Security Element Prompt is a string that contains the prompt to show the user when a secure element is not found. For example "Please insert the security element for Service ABC". If null, the user is not prompted.
- The
removeCredential() method returns true if the credential was removed and false if the user canceled the operation, and may throw the following exceptions: IllegalArgumentException, UserCredentialManagerException, or SecurityException. Please refer to the specification for more information on exceptions.
Attempting to add or remove a certificate to/from the local store results in SATSA prompting the user for permission, by displaying the certificate's name and/or URL, and even maybe prompting for a valid Personal Identification Number (PIN). How this prompt is presented to the user is implementation specific.
In addition, SATSA follows the security/permission policy of the underlying runtime environment, for example in MIDP 2.0, it must follow the permission model that is based on security policies and JAD or MANIFEST file entries; MIDP 2.0 permissions are explained later in this article.
As previously mentioned, the above PKI operations, generating a CSR and managing the certificate local store, are not really intended for typically application usage, and instead are intended for a trusted certificate management client application that is likely provided by a CA and that knows how to coordinate with server side functions.
In network protocols, basic data integrity checks are performed by utilizing checksums or cyclic-redundancy checks. But these methods are weak when compared to secure cryptographic hash-functions.
Also referred to as a digital fingerprint, a message digest is a fixed length sequence of numbers, a condensed and faithful representation of a message. Generating a message digest doesn't alter the message, and any change to the original input message will result in a different message digest. Verifying data integrity using digests is accomplished by simply recalculating the digest then comparing it to the original one. Calculating a message digest is done using a using a one-way hash function such as RSA Security MD5 or NIST's SHA-1. This is illustrated next:
 |
|
Figure 4: Message Digests
|
The SATSA message digest API is based on a subset of the J2SE CRYPTO API and the MessageDigest class. As in J2SE message digest generation, in SATSA there are three high-level steps:
- Initialize the
MessageDigest using an algorithm.
- Calculate (update) the digest from a block of bytes that comprise the signature.
- Generate the digest.
A couple of things need to be known ahead of time before digests can be generated:
- The message digest algorithm to use.
- The input data (byte array) to the hash function.
- If verifying, the digest to compare to.
The following code snippet calculates a message digest. Note that SHA digests are 160 bits (20 bytes), while MD2 and MD5 digests are 128 bits (16 bytes) in length:
Generating a Message Digest
static String digestAlgo = "SHA-1";
static int shaDigestLen = 20;
byte[] message = "..."; // original message
byte[] newDigest = new byte[shaDigestLen];
try {
MessageDigest md;
md = MessageDigest.getInstance(digestAlgo);
md.update(message, 0, message.length);
md.digest(newDigest, 0, shaDigestLen);
} catch (Exception e) {
// Handle NoSuchAlgorithmException or DigestException
...
}
|
The above code snippet calculates a digest that is stored in newDigest for message, using the SHA-1 algorithm. Computing a message digest may result in the following exceptions being thrown: NoSuchAlgorithmException and DigestException. Please refer to the specification for more information on exceptions.
While the J2SE message digest API provides the method isEqual() to compare two digests for verification, SATSA doesn't provide such method and instead a small test loop is used:
Verifying a Message Digest
byte[] originalDigest = "...";
// Verify the calculated digest against the original
// message digest.
boolean verifyOK = true;
for (int i=0; i<shaDigestLen; i++) {
if (originalDigest[i] != newDigest[i]) {
verifyOK = false;
break;
}
}
// Are the digests the same?
if (verifyOK == false) {
// Data integrity test failed...
}
|
The message digest algorithm names are specified in the Java Cryptography Architecture API Specification and Reference, Appendix A: Standard Names. The two most popular cryptographic hash algorithms are:
SHA-1 indicates a SHA-1 signature algorithm.
MD5 indicates a MD5 signature algorithm.
The SATSA specification recommends that at least SHA-1 is supported.
A digital signature provides a mechanism for authentication and non-repudiation, as well as strong data integrity. Let's look at a couple of scenarios:
A user must share a sensitive document with a given recipient. In this case the recipient needs proof the document in fact came from the expected user. Before sharing the document, the sender signs the document using his private key. The sender had previously shared his public key with the recipient, who uses it to verify sender's signature.
A user must prove its identity before it is allowed to consume some services on the Internet. Typically usernames and passwords have been used for this. Because a digital signature contains signed (protected) information such as distinguished names that uniquely identifies a user, digital signatures provide a very strong approach to authentication.
A transaction, monetary or not, is initiated by a user from a J2ME application. This transaction requires support for non-repudiation, which means that once the transaction is approved by the user and processed on the other end, it cannot be repudiated or rejected on the basis that "it never happened or is not valid." Non-repudiation can be guaranteed by signing the transaction using a digital signature. Non-repudiation-type signatures are special because they are indented to legally bind a user to a transaction.
When transmitting sensitive information, detecting data tampering is important. Because a digital signature is based on signed message digests, the result is authenticated, strong (cryptographic) data integrity and data tampering detection.
A digital signature is a message digest that has been encrypted using a private key. As with message digests, generating a digital signature doesn.t alter the original message. The result is an authenticated message with integrity. It is important to note that authentication in this case is indirect and refers to .origin authentication or verification. instead of authenticating a person per-se. In other words, a signed message proves that the message came from a trusted person, assuming the person has properly safeguarded the private key.
As previously mentioned, digitally signing a message or document is the same as encrypting the document.s message digest using the signer.s private key. In the case of SATSA this is accomplished by using one of the locally stored private keys generated during certificate generation process, as previously covered. A message signed with a private key can be verified with the corresponding public key; because public key certificates are shared to users on a need to know basis, only those end-entities can verify the signature. This is illustrated next:
 |
|
Figure 5: The Signing Process
|
As you can see, the process of signing is very similar to generating a message digest, except for the encryption piece.
The SATSA signature APIs can be found in the PKI CMSMessageSignatureService class. The class splits signature generation APIs into 1) authentication, and 2) non-repudiation, mirroring the two supported key usages that we saw during the key enrollment process (generateCSR). These two APIs are very similar both in their method signature and how they work. The following bullets highlight some general information about the SATSA signature APIs, followed by some code examples:
- Method
authorize() is used for the generation of signatures for authentication purposes.
- Method
sign() is used for the generation of signatures for non-repudiation purposes.
- The signature methods require a private key and algorithm for signature generation.
- When creating a digital signature in SATSA, the private key to use is based on two things: the type of signature to generate (sign vs. authorize), and the specified CA distinguished name (DN). The specified CA DN must be properly formatted, as defined in the LDAP distinguished names, otherwise an
IllegalArgumentExceptionis thrown. If the CA DN is null, the implementation will prompt the user for assistance in choosing the certificate to use. If the key is not found, the implementation will prompt the user using the defined securityElementPrompt, if one defined; if no certificate can be selected a CMSMessageSignatureServiceException is thrown.
- SATSA digital signatures are encoded based on the Cryptographic Message Syntax (CMS).
- The application can control the signature format, or what is included in the signed content, through the
options parameter. The signed content and the public key certificate can be optionally included in the signature, if supported by the implementation.
- In some cases the message to sign is displayed to the user prior to signing. For example, when signing strings, SATSA implementations are required to display the string to the user prior to signing. This is not the case when signing raw byte arrays.
- Because non-repudiation signatures are typically used for legal or binding purposes, the SATSA implementation prompts the user for a PIN for every non-repudiation signature to be generated.
- The signature APIs may throw the following exceptions:
CMSMessageSignatureServiceException, IllegalArgumentException, SecurityException, UserCredentialManagerException. Please refer to the specification for more information.
Signing Content for Authentication Purposes
Two methods are provided to generate signatures for authentication purposes; these methods use private keys that were marked for authentication during key generation. One of methods is used to sign a message in a byte array while the other is to sign a message in a String. As previously mentioned the result is a signature encoded in CMS format.
The following code snippet shows the method used to sign an input byte array for authentication purposes:
Signing a byte Array for Authentication Purposes
String securityElementPrompt = null; // Don't prompt
byte[] signature;
byte[] byteArrayToSign = "...";
String myCaDN = "..."; // The CA DN, from the certificate
String[] caNames = new String[] { myCaDN };
try {
// Sign the specified byte array. Include the
// certificate and content as well.
signature = CMSMessageSignatureService.authenticate(
byteArrayToSign,
CMSMessageSignatureService.SIG_INCLUDE_CERTIFICATE
|CMSMessageSignatureService.SIG_INCLUDE_CONTENT,
caNames,
securityElementPrompt);
} catch (Exception e) {
// Handle CMSMessageSignatureServiceException or
// IllegalArgumentException or
// SecurityException or
// UserCredentialManagerException
...
}
|
The above code snippet specifies options that generate a signature that includes the signed content (SIG_INCLUDE_CONTENT), and the public key certificate (SIG_INCLUDE_CERTIFICATE) for key distribution purposes. There are rules about what and when the content and certificate can or must be included. Please refer to the SATSA specification or Javadoc for more information. The above snippet assumes the CA distinguished name been extracted from a corresponding public key certificate or some store. SATSA will then attempt to find the matching private key, and will throw an exception (CMSMessageSignatureServiceException) if not found.
The following method is exactly as the previous one but instead it signs an input String:
Signing a String for Authentication Purposes
String stringToSign = "...";
String securityElementPrompt = null; // Don't prompt
byte[] signature;
String myCaDN = "..."; // The CA DN, from the certificate
String[] caNames = new String[] { myCaDN };
try {
// Sign the specified string. Include the certificate
// and content as well.
signature = CMSMessageSignatureService.authenticate(
stringToSign,
CMSMessageSignatureService.SIG_INCLUDE_CERTIFICATE
|CMSMessageSignatureService.SIG_INCLUDE_CONTENT,
caNames,
securityElementPrompt);
} catch (Exception e) {
// Handle CMSMessageSignatureServiceException or
// IllegalArgumentException or
// SecurityException or
// UserCredentialManagerException
...
}
|
In this version of the authenticate signature method, the string is displayed to the user prior to signing.
Signing Content for Non-Repudiation Purposes
Signing for the purpose of non-repudiation is accomplished by using the method sign(). This method work exactly as the method authenticate() used to sign a String, but uses private keys marked for non-repudiation during key generation:
Signing for Non-Repudiation Purposes
String stringToSign = "...";
String securityElementPrompt = null; // Don't prompt
byte[] signature;
String myCaDN = "..."; // The CA DN, from the certificate
String[] caNames = new String[] { myCaDN };
try {
// Sign the specified string. Include the certificate
// and content as well.
byte[] signature = CMSMessageSignatureService.sign(
stringToSign,
CMSMessageSignatureService.SIG_INCLUDE_CERTIFICATE
|CMSMessageSignatureService.SIG_INCLUDE_CONTENT,
caNames,
securityElementPrompt);
} catch (Exception e) {
// Handle CMSMessageSignatureServiceException or
// IllegalArgumentException or
// SecurityException or
// UserCredentialManagerException
...
}
|
As with authenticate(), the SATSA implementation will display the contents of the string to the user prior to signing. The only difference between the methods sign() and authenticate() is the type of certificate (key) used to generate the signature; method authenticate() uses non-repudiation type of keys.
About Time-Stamping
Combining a digital signature with a digital time stamp from a Time Stamping Authority (TSA) enhances non-repudiation since it provides proof that the signed content existed at some point-in-time, and that have not changed since then. SATSA doesn't provide time stamping functionality, but nothing prevents you from time stamping your content by directly interfacing with a TSA.
|
Once content has been signed, it is sent to a recipient who will verify the signature, providing for authentication and non-repudiation, with the added benefit of data tampering detection.
Verifying Digital Signatures
When receiving a signed message or document, its signature should be verified prior to the message's consumption.
Figure 5 illustrated how a digital signature is generated and verified. Signature verification is about decrypting the original message digest that was sent with the message using the sender's public key, recalculating the message digest, and then comparing it to the original message digest.
The SATSA signature verification API is based on a subset of the J2SE CRYPTO API. As in the J2SE signature verification API, SATSA signature verification uses the Signature, KeyFactory, KeySpec (and subclasses) and PublicKey classes. But there is a difference; in the J2SE CRYPTO API the Signature class is used for both signature generation and verification, but in SATSA the Signature class is only used for signature verification, while generation is done using class CMSMessageSignatureService as previously explained.
As in J2SE signature verification, in SATSA there are three high-level steps:
- Initialize the
Signature using a public key and an algorithm.
- Calculate (update) the signature from a block of bytes that comprise the signature.
- Verifying (compare) the calculated signature against a specified signature.
A couple of things need to be known ahead of time before signatures can be verified:
- The public key and algorithm to use to calculate the signature. This information is typically found in the public key certificate.
- The algorithm used to generate the original signature. This information is typically found in the public key certificate.
- The original message and related signature to verify. This information may be sent over a network connection, email or in other application-specific channels.
The following code snippet shows how to verify a digital signature sent.
Verifying a Digital Signature using SATSA
// Signature
byte[] signedMessage = "..."; // sent by sender
byte[] messageSignature = "..."; // sent by sender
// Public Key
String sendersPublicKeyAlgo = "RSA";
byte[] sendersEncodedPublicKey = "..."; // sent by sender
// Create an X.509 encoded Key specification from the
// encoded public key.
X509EncodedKeySpec pks = new X509EncodedKeySpec(sendersEncodedPublicKey);
try {
// Get an instance of the key factory to generate a
// public key object
KeyFactory kf;
kf = KeyFactory.getInstance(sendersPublicKeyAlgo);
PublicKey sendersPublicKey = kf.generatePublic(pks);
// Generate the signature from the sender's PK
Signature signature;
signature = Signature.getInstance(sendersPublicKey.getAlgorithm());
signature.initVerify(sendersPublicKey);
// Calculate signature from message and public key
signature.update(signedMessage, 0, signedMessage.length);
// Verify the signature
boolean signatureValid;
signatureValid = signature.verify(messageSignature);
if (signatureValid = false) {
// Signature didn't verify...
}
} catch (Exception e) {
// Handle NoSuchAlgorithmException or
// InvalidKeySpecException or
// InvalidKeyException or
// SignatureException.
...
}
|
The above code snippet assumes the message and signature to verify, as well as the encoded public key and algorithm, are all available. The public key is a byte array containing the ASN.1 encoded type SubjectPublicKeyInfo that contains the public key and algorithm. The public key and name of the algorithm to use for the signature verification is typically taken directly from the public key certificate. Signature algorithm names are specified in the Java Cryptography Architecture API Specification and Reference, Appendix A: Standard Names, and use the following form:
<Digest>with<Encryption>
Where Digest (the signature algorithm) could be MD2, MD5, or SHA1, and the Encryption algorithm RSA or DSA. For example:
-
SHA1withRSA indicates a SHA-1 signature algorithm with the RSA encryption algorithm.
-
MD5withRSA indicates a MD5 signature algorithm with RSA Encryption signature algorithm.
The SATSA specification recommends that at least SHA1withRSA is supported.
Note that in practicality before trusting a public key, some verification must take place such as verifying the whole certificate path, verifying CRLs, and verifying the certificate's fingerprint. SATSA doesn't provide much functionality to accomplish such verifications and/or extract public key information from a digital certificate.
The above signature code snippet may throw the following exceptions: NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException. Please refer to the SATSA specification and/or Javadoc for more information.
Data Confidentiality - Using Ciphers for Data Encryption
You may be connecting to corporate resources, or exchanging personal or other sensitive data; in both of these cases keeping information secret, or encrypting data, is of chief importance. In the web, data confidentiality is achieved by using secure network protocols such as HTTPS and TSL/SSL. In SATSA, encryption is accomplished by using a subset of the J2SE CRYPTO API and the Cipher class.
As previously mentioned, symmetric cryptography is best suited for data encryption, while asymmetric cryptography is best suited for authentication, non-repudiation and data-integrity through the use of digital signatures. SATSA encryption API supports both symmetric and asymmetric encryption as we will see shortly. Also always keep in mind that encryption is a very expensive, time consuming operation.
Using Ciphers for Public Key (Asymmetric) Encryption
Let's revisit some concepts. In asymmetric or public key cryptography public keys are freely distributed as needed. In this model data is encrypted using a public-key, and is decrypted using the corresponding private-key, and vice-versa. The idea is that if I need to send a short confidential message to Henry, I would encrypt the message using his public key. At a later time he would use his private key to decrypt the message.
The following diagram shows both signing and encryption, which are separate but related steps, and the results of these steps combined into a single encrypted, signed message.
 |
|
Figure 6: Creating an Encrypted, Signed Messaging using Public Key Cryptography
|
Decrypting is opposite to data encryption. Here an encrypted message is decrypted using the recipient's private key, and the message digest (digital signature) is decrypted using the sender's public key, then verified by comparing the signatures as previously covered:
 |
|
Figure 7: Decrypting a Signed Messaging using Public Key Cryptography
|
Please note that while SATSA provides support for asymmetric encryption, it doesn't for provide support for PrivateKey and asymmetric (public key) decryption.
As in J2SE message encryption/decryption process, in SATSA there also are three high-level steps:
- A
Cipher is generated and initialized using an algorithm and optionally a mode and a padding scheme. The Cipher mode is set for data encryption or decryption.
- Call the
Cipher.update() method if encrypting or decrypting multiple-parts.
- Call the
Cipher.doFinal() method if encrypting or decrypting a single-part, or finishing a multi-part encryption/decryption.
A Cipher is generated by calling method Cipher.getInstance(String transformation), passing as argument the transformation (the algorithm and optionally a mode and a padding scheme) to use. Transformation names are specified in the Java Cryptography Architecture API Specification and Reference, Appendix A: Standard Names, and uses the
following form:
algorithm/mode/padding
-Or-
algorithm
...Here, the first form fully describes the cipher algorithm, mode and padding to use, and the latter describes an algorithm but uses default values for the mode and padding.
The following code snippet briefly shows how to encrypt data using a public key:
Using Cipher to Encrypt Data using a RSA (Asymmetric Encryption)
// Message to encrypt
byte[] plainText = "...";
// Public Key
String keyAlgo = "RSA";
byte[] someonesEncodedPublicKey = "...";
try {
// Create an X.509 encoded Key specification from the
// encoded public key.
X509EncodedKeySpec pks;
pks = new X509EncodedKeySpec(someonesEncodedPublicKey);
// Get an instance of the key factory to generate a
// public key object
KeyFactory kf = KeyFactory.getInstance(keyAlgo);
PublicKey publicKey = kf.generatePublic(pks);
// Encrypt
byte[] cipherText = new byte[1024];
Cipher cipher;
cipher = Cipher.getInstance(publicKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
cipher.doFinal(plainText, 0, plainText.length, cipherText, 0);
} catch (Exception e) {
// Handle BadPaddingException or
// InvalidKeyException or
// IllegalStateException or
// InvalidKeySpecException or
// IllegalBlockSizeException or
// InvalidAlgorithmParameterException or
// NoSuchPaddingException or
// NoSuchAlgorithmException or
// ShortBufferException
...
}
|
While the J2SE Cipher class provides the method getOutputSize() that returns the buffer size that would be the needed to hold the resulting encrypted data (ciphertext), no such method was included in SATSA. How to determine the expect size of encrypted (ciphertext) or decrypted (back to plaintext) data is outside the scope of this article. Allocating a ciphertext buffer that is too small results in a ShortBufferException.
Using Ciphers for Symmetric Encryption
At first, symmetric encryption seems simpler than its asymmetric counterpart because encryption and decryption is based on a single secret key.
 |
|
Figure 8: Symmetric Encryption/Decryption Process
|
But using symmetric encryption could be more involved due to the number of details to be considered, such as cipher types, transformations (algorithms, modes and padding), and the number of bits to be processed at a time. For example, symmetric ciphers can either be stream or block ciphers. Stream ciphers, which can be faster than block ciphers, typically operate on small units of plaintext, such as bits. On the other hand, block ciphers operate on large blocks of data. And there are modes which have implications on padding, and the use of initialization vectors.
And there are other considerations when using symmetric cryptography such as the common amateur crypto mistake pointed out by Bruce Schneier where the same keystream must never be used to encrypt two different documents, otherwise the encryption can be broken just by XORing the two ciphertext streams together. All these considerations are outside the scope of this article and you must spend the time to understand these.
A key issue related to symmetric cryptography is key distribution/sharing, where a secure way to share the secret key is needed. Because of the flexibility offered by public key systems in this matter, the typical way to share secret keys is using public key encryption - this hybrid approach of using public key cryptography for symmetric key distribution is also referred to as secure key establishment in a secret-key system. In this approach the secret key, which typically is a short session-lived key that is created just for the duration of an encrypted conversation, is encrypted using public key cryptography. Once key establishment has occurred, symmetric cryptography is used for the rest of the conversation.
From the API usage perspective, symmetric encryption is done similarly to the asymmetric encryption covered in the previous section, except for the following:
- The use of
SecretKey object, to encapsulate the symmetric secret key.
- Depending on the cipher type and mode, an Initialization Vector (IV) is used to ensure proper generation of ciphertext.
The following code snippet briefly shows how to encrypt data using a private key:
Using Cipher to Encrypt Data using a Symmetric Encryption
String algo= "..."; // Symm. Cipher algorithm (DES, etc)
byte[] secretKey = "..."; // Secret key bytes
String secretKeyAlgorithm = "..."; // Secret key algo.
byte[] iv = "..."; // The initialization vector
byte[] plainText = "..."; // The plain text
try {
// Create the (secret) key. This method assumes
// secretKey is a raw secret key represented as a byte
// array, with no key parameters associated, such as
// DES or Triple DES keys.
Key key = new SecretKeySpec(secretKey, 0, secretKey.length, secretKeyAlgorithm);
// Create the cipher. If an initialization vector was
// specified, use it.
Cipher cipher;
cipher = Cipher.getInstance(algo);
if (iv == null) {
cipher.init(Cipher. ENCRYPT_MODE, key);
} else {
IvParameterSpec ivps;
ivps = new IvParameterSpec(iv, 0, iv.length);
cipher.init(Cipher.ENCRYPT_MODE, key, ivps);
}
// Encrypt the single-part plaintext - i.e. for multi-
// part plaintext, the update() method would be called
// as necessary before finishing by calling doFinal().
int ciphertextLength = ...;
byte[] cipherText = new byte[ciphertextLength];
cipher.doFinal(plainText, 0, plainText.length, cipherText, 0);
} catch (Exception e) {
// Handle BadPaddingException or
// InvalidKeyException or
// IllegalStateException or
// InvalidKeySpecException or
// IllegalBlockSizeException or
// InvalidAlgorithmParameterException or
// NoSuchPaddingException or
// NoSuchAlgorithmException or
// ShortBufferException
...
}
|
The above code snippet must handle the same exceptions as covered in the previous asymmetric encryption code snippet.
Decrypting is done similarly to encrypting except for:
- The
Cipher is set to decrypt mode during its initialization.
- The methods
update() and doFinal() input and output arguments are reversed when compared to encryption: for input we have the ciphertext and for output the plaintext:
Using Cipher to Decrypt Data using a Symmetric Encryption
:
:
// Create the cipher. If an initialization vector was
// specified, use it.
Cipher cipher = Cipher.getInstance(algo);
if (iv == null) {
cipher.init(Cipher. DECRYPT_MODE, key);
} else {
IvParameterSpec ivps;
ivps = new IvParameterSpec(iv, 0, iv.length);
cipher.init(Cipher.DECRYPT_MODE, key, ivps);
}
// Decrypt
byte[] plainText = new byte[...];
cipher.doFinal(ciphertext, 0, ciphertext.length, plainText, 0);
|
When using ciphers in general, the following exceptions must be handled:
BadPaddingException, InvalidKeyException, IllegalStateException, InvalidKeySpecException, IllegalBlockSizeException, InvalidAlgorithmParameterException, NoSuchPaddingException, NoSuchAlgorithmException, ShortBufferException. Please refer to the SATSA specification and/or Javadoc for more information.
Applications requiring the SATSA message signature service must request permission by making an entry in the JAD or MANIFEST file:
MIDlet-Permissions: javax.microedition.securityservice.CMSMessageSignatureService
|
This service is available in the operator, manufacturer, and third-party trusted domain.
In addition, a SATSA implementation always prompts the user for acknowledgement, for activities related to certificates such as storing and removing a certificate from the store, generating a certificate enrollment request, and signing messages. Accessing a security element may require user authorization through the use of Personal Identification Numbers.
Security is one of the most important aspects in wireless mobility; when accessing personal information, or accessing corporate resources, security plays a very important role.
This article covered a lot of information related to security and SATSA - from background on PKI and cryptography, to use-cases, the SATSA APIs and how to use them. But this is just the tip of the iceberg. Any person who is responsible for designing and developing secure J2ME applications must spend the right amount of time to understand the underpinnings of PKI, and cryptography in general. This article should provide a good start on your endeavor into J2ME security.
Thanks to Zhiqun Chen, Saqib Ahmad and Gary Adams from Sun Microsystems, Wouter Habraken from Raak Technologies, and Roland van Rijswijk from AET Europe, for their help and feedback while writing this article.
C. Enrique Ortiz is a software architect and developer, and a wireless mobility technologist and writer. He is author or co-author of many publications, a co-designer of Sun Microsystems' the Mobile Java Developer Certification Exam, and has been an active participant in the wireless Java community and in various J2ME expert groups. Enrique holds a B.S. in Computer Science from the University of Puerto Rico and has more than 15 years of software engineering, product development, and management experience.
|
|