Code Security
All JME code is written in
Java, and Java is a memory managed language that prevents buffer and
integer overflows and direct manipulation of memory and the hardware.
The virtual machine makes this security magic possible by verifying
every instruction before execution and ensuring that all application
code handles memory and objects safely. Not having to worry about
memory-related security issues is a real boon to developers, but it
doesn’t mean that they are free and clear. Application code can still
use the network and local storage insecurely, and the virtual machine
implementation itself might have problems that attackers could exploit
to compromise devices. For example, Adam Gowdiak reported avulnerability
in the Kilobyte Virtual Machine’s verifier that an application could
use to escape the sandbox (http://secunia.com/advisories/12945/). The risk of a JVM error pales in comparison to the risk of writing every JME application in an unmanaged language such as C.
CLDC Security
The
CLDC JSR specifies that JVMs implementing the CLDC configuration must
only load and execute valid Java bytecode. In addition, CLDC JVMs do not
support all of Java’s language features. Specifically, the CLDC 1.1 JSR
says that CLDC must ensure the following:
Class files must
be properly verified and the Java bytecode well formed. All code
branches must follow predictable paths and jump to controlled memory
addresses. Code verification ensures that the application is not able to
execute illegal instructions.
Applications
cannot load custom class loaders or classes of their choosing. If
attackers could load their own classes, they could pull in application
code without the user’s knowledge.
The
API set exposed to applications is predefined. Therefore, applications
cannot use Java reflection to dynamically load classes or access private
methods. By forcing a predefined set, device manufacturers and carriers
know which platform APIs are exposed and how the application will be
able to access the hardware. Device manufacturers and carriers can
always add to the protected set if they want to expose device
model-specific functionality (for example, the camera or a digital
compass).
Native
functionality is prohibited. Java native invocation (JNI) is a
technology used to bridge between native code (such as C/C++) and
managed Java code. Native code executes outside of the JVM and cannot be
monitored. Therefore, JME applications must be prevented from using JNI
and including native extension libraries.
Applications
cannot extend classes in the java.*, javax.microedition.*, and other
manufacturer-specific packages. If malicious applications were allowed
to overload sensitive system classes, they might be able to take
advantage of polymorphism and force system APIs to execute
attacker-supplied code when calling object methods.
All
classes must come from the same JAR file. This requirement prevents
applications from loading and using classes from other applications that
may be installed on a device. This restriction may change when
libraries are introduced as part of MIDP 3.0.
These restrictions aim to stop
applications from running Java code that cannot be managed or accesses
the hardware in unexpected way. MIDP relies on this infrastructure to
build a higher level application sandbox. To enforce these restrictions, CLDC performs “class
file verification” and inspects the Java bytecode to ensure that all
variables are initialized, the actual instructions are legitimate, and
that only valid types are used.
Pre-verification
To have an impact, the CLDC JVM security rules must actually be enforced when the code is installed and executed on the device. Pre-verification
is the process that evaluates application code and creates markings
that will be used by the JVM during installation or runtime
verification. All Java virtual machines perform some sort of
verification process, but only JME performs the verification process at
compilation time—hence, the name per-verification. CLDC doesn’t actually
require pre-verification to be used, but on mobile devices it is
preferred over standard verification, which consumes large amounts of
system resources.
Pre-verification works by
scanning the application’s bytecode and generating a series of
“StackMap” attributes for each code item in an application. The StackMap
includes information about the local variable types being used by each
basic block of the application. Once the StackMap attributes are
generated, they are inserted into the attribute section of each code
attribute in the application. When the application is installed onto the
device, the JVM performs a linear scan of the application’s bytecode
and compares references and object types to the information contained in
the StackMap. The device refuses to load the application if any part of
the comparison fails.
Storing StackMap entries does
marginally increase the size of applications, but enables the
verification algorithm to execute in linear time and with predictable
resource use—important qualities for mobile devices. The algorithm is
resistant to malicious tampering or manufacturing of StackMap entries,
and an invalid or incomplete StackMap will cause the application to be
rejected when a user loads it onto a device.
Use preverify.exe to perform
pre-verification of your applications. Sun’s official reference
implementation is included with the SDK and NetBeans, and both will
automatically perform pre-verification as part of the build process. The
ProGuard obfuscation toolset also includes an alternate implementation
of the pre-verifier. To learn more about pre-verification, see Appendix 1
of the CLDC 1.1. specification.
Application Packaging and Distribution
CLDC requires that all
JME application code be packaged into Java archive files. JAR is a
compressed file format very similar to ZIP. Each application JAR has an
associated Java Application Descriptor (JAD). The JAD file is a simple
text file with a
listing of key/value pairs that describe certain properties of the
application (for example, the application’s author and the location of
the developer’s website). The combination of the JAR and the JAD is what
actually composes a JME application. This differs from standard Java
applications, which do not have JAD files and keep their metadata in the
JAR file. JME JARs still have metadata, but they can grow to be quite
large and be prohibitive to download over slow and costly cellular
links. By putting the metadata in the JAD file, which is much smaller
than the JAR, mobile devices can present the user with a choice before
downloading the entire application.
More on JAD Files
JAD files are an important
part of the MIDP application life cycle and contain application
signatures, permission listings, and other important security
information. Each line in a JAD file consists of an attribute name and
attribute value. A sample JAD follows:
MIDlet-1: iSEC Partners Maps, , com.isecpartners.jme.iSECNavigator
MIDlet-Jar-URL:
http://www.isecpartners.com/applications/v1/isecnav.jar
MIDlet-Jar-Size: 6479
MIDlet-Name: iSEC Maps
MIDlet-Permissions: javax.microedition.io.Connector.http
MIDlet-Icon: icon16x16.png
MIDlet-Version: 1.0.2
MIDlet-Vendor: iSEC Partners
MIDlet-Install-Notify:
http://www.isecpartners.com/applications/v1/cust
This JAD file provides the
name of the application (iSEC Maps), the vendor (iSEC Partners), and
the location of the actual JAR file. After the JAD is downloaded, this
information will be shown to the user so they can decide whether or not
to install the application. Also note that the JAD is requesting the
javax.microedition.io.Connector.http permission by using the
MIDlet-Permissions attribute. Phones could use this as part of a
permission UI at installation time.
Most JAD attributes are
defined in the MIDP JSR and the optional JAD JSRs. Vendors may define
additional attributes that are unique to their device. Samsung, for
example, defines the MIDlet-Touch-Support option for indicating that
your application should be displayed full screen. None, or very few, of
the vendor-specific options have an actual impact on security.
The information in the JAD can
be duplicated in the application manifest and the JAD must match the
manifest exactly. In MIDP 1.0, JAD information took priority over
MIDP information. MIDP 2.0 requires the match. Otherwise, the JAD could
claim the application has different metadata than it actually does.
Other security vulnerabilities related to JAD file parsing have also
been reported. For example, Ollie Whitehouse from Symantec discovered
that embedding character return characters in a JAD can cause some
phones to display incorrect information on the installation screen.
Signatures
Devices uses code signatures
to verify the integrity and origin of applications and then use this
information to decide how much to trust a given application. Standard
Java applications also use signatures for this purpose, but the
signature is embedded in the JAR file. JME signatures are attached to
the JAD, and the rules are slightly different: Every application can
only have one signer, and any changes to the JAR file invalidate the
application’s signature.
Two attributes are used
to express JAD signatures: MIDlet-Certificate-X-Y and
MIDlet-Jar-RSA-SHA1. The MIDlet-Certificate attribute describes the
certificate chain. X is the chain number, and Y is the certificate’s
position in the chain. A value of 1 for Y denotes the leaf certificate.
The MIDlet-Jar-RSA-SHA1 is an RSA-encrypted SHA-1 hash of the JAR file
and will be verified against the certificate described in the JAD and
the device’s certificate store. Both entries are Base-64 encoded. Here
are sample JAD signature nodes:
MIDlet-Certificate-1-1:
MIICGTCCAYKgAwIBAgIESjmCFjANBgkqhkiG9w0BAQUFADBRMQwwCgYDVQQGEwNVU0ExC
zAJBgNVBAgTAkNBMQ8wDQYDVQQKEwZpc2Vjb3UxDTALBgNVBAsTBGlzZWMxFDASBgNVBA
MMC2lzZWNfc2lnbmVyMB4XDTA5MDYxNzIzNTM1OFoXDTA5MTIxNDIzNTM1OFowUTEMMAo
GA1UEBhMDVVNBMQswCQYDVQQIEwJDQTEPMA0GA1UEChMGaXNlY291MQ0wCwYDVQQLEwRp
c2VjMRQwEgYDVQQDDAtpc2VjX3NpZ25lcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCg
YEAiTLVnE4/EFFvJORxa0/wFYi8/QZfufiu4QGFdB4jJchKalxDe1UoqorbEDiowcUw7M
AFoVR6yKOeHRZVTuKU4uq4fti/XcmwyML7loHw39Pd097384PK745DGUirDCqf6Dak1Tq
NG9EjicQKXDNaAd98xaEJGpeqpOHhN5K0LokCAwEAATANBgkqhkiG9w0BAQUFAAOBgQA8
IxC1OLw86yt8U2u9ufogaD7comUZyg+USjI0pkdaUVTRY+Xd+QCNh6PJpwItH8ImuioRs
elLJH4Tel7KRrXNchJYuoDF+K4ajpc62dpfpIB0FlPhuXFMD5z0E3Mkd4cfWVUIGvE/ZB
7xVBNtZEmINIQjvtKcZG6v6izO5uxilw==
MIDlet-Jar-RSA-SHA1:
hSd7tIqqIh+Aw08DUYvc2OtoMP5DiMsFZbt0M/cjlkaQfvZaEGy061KlvwSSoNF9kPhLT
G1scZnN5j597d5xGuk+WkOzLhUlKwNtZYEDRPnwsiOw56qhvOw2yNQH2gF+Cj9VR6dWL5
1MvnFk8PJeU5Q2Uey0NeROFlQ6F/i1Shc=
Obtaining a Signing Key
To
generate signatures, you will need a certificate and a public/private
keypair. These can be purchased from different code-signing
Certification Authorities (CA). Despite everyone’s best intentions,
getting a signing key that works on all devices and all networks around
the world is very difficult. Each carrier has a unique application
approval process and rules; often these rules are enforced by requiring
code to be signed with a certificate from a particular CA. Sun recently
introduced the Java Verified program, which seeks to ease developers’
pain and make the JME ecosystem more consistent with standard CAs and
testing procedures. It is not clear yet if it is going to be a success.
To find out more information, visit http://javaverified.com/. For a more traditional approach, visit the mobile development website of your target carrier.
Of course, paying for a
certificate is no fun if you just want to learn about device security.
Therefore, feel free to generate test certificates using the Java SE
keytool.exe tool. These self-signed certificates can be used to sign
applications and deploy them to emulators. These signatures will not be
accepted by real-world devices. Follow these steps to create a key for
signing:
Install
the Java Runtime Environment (JRE). If you have been running NetBeans
or any Java applications, this will already be installed. If not,
download the JRE from www.java.com.
Open a command prompt (Start | Run and type cmd.exe).
Change to the JRE bin directory (for example, C:\Program Files\Java\jdk1.6.0_13\jre\bin).
Generate a keypair by running the following command:
keytool -genkey -keyalg RSA -keysize 1024 -alias SigKey
-keystore c:\drop\keystore
This command will create a new
key with the alias “SigKey” and a new keystore file at c:\drop\keystore.
During the key-creation process, you will be asked for some
information. Because this is a self-signed certificate, feel free to
enter whatever you wish. Also make sure to specify a secure password for
the keystore. This password is used to encrypt the keystore and ensure
that no one else can access it.
Signing JME Applications
Now that you have a signing
key, it is possible to actually sign JME applications. To do so, use
the jadtool.exe program that comes with the JME SDK. This tool takes a
JAD and a JAR as input, calculates the signature, and then updates the
JAD for you. To generate a signature, follow these steps:
1. | Install the JME SDK. These instructions assume that you have installed version 3.0.
|
2. | Open a command prompt (Start | Run and type cmd.exe).
|
3. | Change to the JME SDK bin directory (for example, C:\Java_ME_platform_SDK_3.0\bin).
|
4. | Run the jadtool and add the public key of your signing certificate into MyApp’s JAD file using the following command:
jadtool -addcert -alias SigKey -keystore c:\drop\keystore -inputjad myapp.jad -outputjad myapp-key.jad
|
5. | This
command refers to the key that was created earlier, so make sure that
the key alias and keystore file path match up. After running this
command, you can open the generated myapp-key.jad file and see that the
MIDlet-Certificate-1-1 attribute has been added.
|
6. | Now sign the application with this command:
jadtool -addjarsig -alias SigKey -keystore c:\drop\keystore -inputjad myapp-cert.jad -outputjad myapp-signed.jad -storepass password -keypass password -jarfile myapp.jar
|
7. | You
will need to substitute the appropriate password values for the
storepass and keypass parameters and make sure that the filenames point
to your actual application.
|
Both keystore management
and signing can be performed using the NetBeans IDE. To manage
keystores, use Tools | Keystore. To control signing, right-click on a
project, open the Properties panel, and select the Build\Signing
category.
Distribution
How JME applications
are distributed varies by device and network. Almost every carrier has
some sort of application store that is accessible via the phone.
However, there are many other websites that offer JME applications, and
unlike with the iPhone, users are not locked in to getting applications
from just their carrier. Naturally, this increases the risk of users
being tricked into installing malicious applications from questionable
sources.
Installation
All
MIDP 2.0 devices must support Over-The-Air (OTA) application
installation. Of course, the implementation will vary between vendors,
but they all follow the same pattern. To install an application OTA, the
user visits a website and downloads the JAD file. The device parses the
file and displays the application’s information to the user. At this
point, the user must be presented with an option of canceling
installation. If the user chooses to proceed, the device will download
the application’s JAR file from the URL specified in the JAD file and
install the application into the local package manager.
Note that both the JAD and
JAR files are downloaded using the cleartext and non-integrity-protected
HTTP protocol. The use of signatures mitigates the risk that an
attacker could modify the application as it is downloaded. Of course,
the attacker could just remove the signature, but that would hopefully
cause the user to not accept the application’s installation or cause the
device to run the application with reduced privileges.