Sorry about the red box, but we really need you to update your browser. Read this excellent article if you're wondering why we are no longer supporting this browser version. Go to Browse Happy for browser suggestions and how to update.

RIM Cryptographic API

The RIM Cryptographic API is a collection of classes that help you create security solutions for your app. Using the RIM Cryptographic API, you can encrypt and decrypt data, digitally sign and verify data, work with secure connections, and manage cryptographic keys.

The CLDC and MIDP specifications of Java ME do not define a cryptographic API. In creating the RIM Cryptographic API, RIM chose to not follow the Java security model because mobile devices have little room for multiple cryptographic algorithm providers. In addition, compile time checking is more appropriate for embedded development than runtime checking.

The RIM Cryptographic APIs are in the net.rim.device.api.crypto package.

Overview of cryptography

For electronic data transmission, cryptography is used to provide highly secure, authenticated communication between a sender and a receiver. It encompasses a wide variety of processes from complex protocols and algorithms to the simple scrambling of letters of text.

While cryptography is central to the security and integrity of transmitted data, it is important to realize that no protocol is entirely secure. Protocols and algorithms are constantly being assaulted by increasingly intelligent criminals with increasingly powerful computers. Cryptographers are constantly improving routines and algorithms. For example, cryptographers increase key sizes to increase the amount of work required by the hacker.

There are two main types of cryptosystem:

  • Symmetric: This cryptosystem is also known as block cipher cryptography. It uses the same key for encryption and decryption. The advantages are that encryption and decryption are relatively fast. The disadvantage is that it can be difficult to securely distribute the keys.
  • Asymmetric: This cryptosystem is also known as public key cryptography. The sender encrypts data with a public key, and then the receiver decrypts it with a private key that they already have. The keys are easy to distribute, but the encryption and decryption is relatively slow.

Symmetric and asymmetric cryptosystems are often used together. For example, two people, user1 and user2, need to do a lot of communication that must be encrypted. They need a fast cryptosystem so that their communication is quick, but they need an easy way to create a key. User1 uses user2's public key to encrypt a symmetric key. User1 then sends the symmetric key to user2. User2 decrypts the symmetric key with her private key (that only she knows). Now user1 and user2 share a symmetric key. They can send encrypted data to each other using a fast, symmetric cryptosystem.

The three main goals of cryptography are confidentiality, integrity, and authentication.

Confidentiality: The most common use of cryptography in the business environment is for data encryption. Encryption is the disguising of a message in such a way that its true meaning is shielded until it is deciphered by the intended receiver. There are many different ways to accomplish this. The most common approach is through the use of ciphers. In a typical scenario, a message is encoded using a predetermined and agreed upon protocol and cipher. The resulting message, called the ciphertext, is then transmitted to the receiver. Once the message is delivered, the receiver decrypts the message using the agreed upon protocol.

Many different encryption algorithms exist. Some are more secure and more practical than others.

Integrity: Data integrity is acheived in modern cryptography by using a hash function to produce a unique "digital fingerprint" of a document. In other words, a complex function is applied to a document to create a unique value. When the message is delivered, the user applies the same hash function to the message. If the resulting values match, then the message likely has not been modified. While even the best hash functions are not guaranteed to produce unique values every time, the chances that two different documents will produce the same value is very unlikely.

Different hashing routines exist for use in different scenarios. A common hashing routine is the MAC. MACs combine encryption keys and hashing functions to allow users to transmit secure, key-dependent hash values.

Authentication: Authentication is a way to verify who you are communicating with. One common protocol combines a digital signature along with a private key encryption routine to create a type of digital stamp. In order to decrypt the message, the receiver must decrypt the digital stamp using the sender's private key. Assuming the sender's private key has not been compromised, this method assures the authenticity of the digital signature.

Overview of the RIM Cryptographic API

The RIM Cryptographic API contains the following APIs.

Secure Messaging API

The Secure Messaging API is an implementation of the CMS standard. CMS specifies standard content types for cryptography. It describes message and transmission formats for those content types. S/MIME is built on CMS. The net.rim.device.api.crypto.cms package provides classes to help you create and manage CMS content types.

Secure Connection API

The Secure Connection API defines protocol functionality that you can use to establish secure communication. You can use SSL, TLS, or WTLS.

SSL: SSL is designed to secure data that is sent over TCP/IP connections; it is used in the implementation of HTTPS. SSL is provided in the net.rim.device.api.crypto.tls.ssl30 package.

TLS: TLS is a standard from the IETF that is based on SSL version 3. TLS was designed to replace SSL and has been widely adopted. TLS is provided in the following packages:

WTLS: WTLS is a layer on top of WAP rather than TCP/IP. Securing wireless communications that use WAP involves using WTLS between the client device and the WAP gateway, and one of SSL or TLS beyond the WAP gateway. WTLS is provided in the net.rim.device.api.crypto.tls.wtls20 package.

Keystore API

A key store is a database that stores cryptographic keys and certificates. Each BlackBerry smartphone has a key store that is preloaded with root certificates for all of the certificate authorities. This practice makes it possible for smartphone users to trust the root certificates, which form the basis for all subsequent chains of trust. Key store classes are provided in the net.rim.device.api.crypto.keystore package.

Certificate API

Certificates are electronic documents that hold keys, along with identifying information. There are several packages to help you manage cryptographic certificates:

Encoder API

Encoding is the process of converting data from one format to another. While it is often part of the encryption process, encoding is not the same as encryption and is generally not secure. Keys are encoded to provide a standard representation, not to protect their identity. Classes for encoding keys and signatures are provided in the net.rim.device.api.crypto.encoder package.

ASN1 API

Most apps use certificates that are produced by a certificate authority. If you need to parse or read certificates yourself, you must use the net.rim.device.api.crypto.asn1 package.

OID API

Object identifiers are managed with the net.rim.device.api.crypto.oid package.

Primitives API

Cryptographic primitives are the keys, MACs, ciphers, unkeyed algorithms such as digests and PRNGs, and other functionality associated with both symmetric and public key cryptography. Cryptographic primitives are provided in the net.rim.device.api.crypto package.

Code sample: Implementing encryption

The following code sample uses Triple DES, which is a symmetric key encryption algorithm. The RIM Cryptographic API simplifies the use of Triple DES to encrypt and decrypt messages.

import java.io.*;
import net.rim.device.api.crypto.*;
import net.rim.device.api.util.*;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.util.*;

public class EncodeDecodeString extends UiApplication implements FieldChangeListener
{
    private ButtonField _encrypt_button;
    private ButtonField _decrypt_button;
    private TripleDESKey _key;
    private TextField _edit_input_string;

    public static void main( String[] args )
    {
        EncodeDecodeString theApp = new EncodeDecodeString();
        theApp.enterEventDispatcher();
    }

    public void fieldChanged(Field field, int context)
    {
        String strCurrentMessage = _edit_input_string.getText();
        if(field == _encrypt_button)
        {
            _edit_input_string.setText(encrypt(strCurrentMessage));
        }
        else
        {
            _edit_input_string.setText(decrypt(strCurrentMessage));
        }
    }

    public EncodeDecodeString()
    {
        MainScreen screen = new MainScreen();
        screen.setTitle(new LabelField("Crypto Demo", 
               LabelField.ELLIPSIS | LabelField.USE_ALL_WIDTH));
        
        _edit_input_string = new BasicEditField("Message:", 
               null, 256, BasicEditField.FILTER_DEFAULT);
        screen.add(_edit_input_string);
        
        _encrypt_button = new ButtonField("Encrypt",ButtonField.CONSUME_CLICK);
        _encrypt_button.setChangeListener(this);
        screen.add(_encrypt_button);
        
        _decrypt_button = new ButtonField("Decrypt");
        _decrypt_button.setChangeListener(this);
        screen.add(_decrypt_button);
       
        pushScreen(screen);
    }

    public String encrypt(String plaintext)
    {
        try 
        {
            _key = new TripleDESKey();
            TripleDESEncryptorEngine encryptionEngine = 
                new TripleDESEncryptorEngine(_key);

            // In most cases, the data to encrypt will not fit into the block
            // length of a cipher. When that happens, use a padding algorithm
            // to pad out the last block. This uses PKCS5 to do the padding.
            PKCS5FormatterEngine formatterEngine = new PKCS5FormatterEngine( 
                  encryptionEngine );

            // Use the byte array output stream to catch the encrypted information.
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

            // Create a block encryptor to help use the triple DES engine.
            BlockEncryptor encryptor = new BlockEncryptor( formatterEngine, 
                  outputStream );

            // Encrypt the data.
            encryptor.write( plaintext.getBytes() );

            // Close the stream. This forces the extra bytes to be padded out if 
            // there were not enough bytes to fill all of the blocks.
            encryptor.close();

            // Get the encrypted data.
            byte[] encryptedData = outputStream.toByteArray();
            String strEncrypted = new String(encryptedData);
            return(strEncrypted);

        } 
        catch( CryptoTokenException e ) 
        {
            System.out.println(e.toString());
        } 
        catch (CryptoUnsupportedOperationException e) 
        {
            System.out.println(e.toString());
        } 
        catch( IOException e ) 
        {
            System.out.println(e.toString());
        }
        return "error";
    }
        
    public String decrypt(String ciphertext)
    {
            try
            {
            // Perform the decryption. Since this is a symmetric algorithm, 
            // use the same key as before.
            TripleDESDecryptorEngine decryptorEngine = 
                    new TripleDESDecryptorEngine(_key);

            // Create the unformatter engine to remove padding bytes.
            PKCS5UnformatterEngine unformatterEngine = 
                    new PKCS5UnformatterEngine( decryptorEngine );

            // Set up an input stream to hand the encrypted data to the 
            // block decryptor.
            ByteArrayInputStream inputStream = 
                    new ByteArrayInputStream( ciphertext.getBytes() );

            // Create the block decryptor passing in the unformatter engine and the 
            // encrypted data.
            BlockDecryptor decryptor = 
                    new BlockDecryptor( unformatterEngine, inputStream );

            // Next, read from the stream. This example reads the data 10 bytes 
            // at a time and then adds that new data to the decryptedData array. 
            // For efficiency in a real situation, you should use a value
            // larger than 10. This example uses a small value to demonstrate 
            // several iterations through the loop.
            byte[] temp = new byte[10];
            DataBuffer db = new DataBuffer();
            
            for( ;; ) 
            {
                int bytesRead = decryptor.read( temp );
                if( bytesRead <= 0 )
                {
                    // No more information to read, so leave loop.
                    break;
                }
                db.write(temp, 0, bytesRead);
            }

            // Make sure that the decrypted data is the same as the data  
            // that was passed into the encryptor.
            byte[] decryptedData = db.toArray();
            String strDecrypted = new String(decryptedData);
            return(strDecrypted);         
        } 
        catch( CryptoTokenException e ) 
        {
            System.out.println(e.toString());
        } 
        catch (CryptoUnsupportedOperationException e) 
        {
            System.out.println(e.toString());
        } 
        catch( IOException e ) 
        {
            System.out.println(e.toString());
        }
        return "error";
   }
}