ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Java vs. .NET Security, Part 3
Code Protection and Code Access Security (CAS)

by Denis Piliptchouk
01/28/2004

This is the third article in a series of Java vs. .NET security comparisons. It deals with the issues of code protection and distribution, and Code Access Security (CAS) mechanisms on those platforms. Previous articles of this series covered configuration and code containment in Part 1, and cryptography support and the mechanisms of communication protection in Part 2.

Once code or an algorithm has been written, it becomes an asset that requires protection. Such a protection is needed not only against theft, but also against unauthorized or unintended use. On the other hand, when somebody purchases a software package, he wants to be confident that he is not dealing with a counterfeit product. To answer all of these challenges, various techniques, broadly divided into cryptography-based and everything else, are employed for code protection and verification.

Code-access security is also known as policy-based security. It allows minimizing the risks of executing certain application code by providing policies restricting it to only a particular, well-defined set of operations that the code is permitted to execute. Of course, the underlying services (platform or application) have to actually carry out those checks for the policy to become effective.

Code Protection: General

Issues discussed in this section are applicable, to a certain degree, to both platforms. They also employ similar protection mechanisms to combat those problems.

The possibility of the reverse engineering of distributed bytecodes needs to be taken into account when planning the security aspects of an application, because bytecode formats are well-documented for both Java and .NET (see also GotDotNet), so any hardcoded data or algorithms may be easily restored with readily obtainable decompiling tools. This point is especially important for avoiding hardcoding user credentials or non-patented algorithms in client-based application modules.

O'Reilly Emerging Technology Conference.

While there is no ideal way around this issue, short of shipping encrypted code and providing just-in-time decryption, an average perpetrator's task may be made harder by using so called obfuscators; i.e., tools that intentionally scramble bytecodes by using unintelligible names and moving entry points around. In addition to the obfuscator tool available with VS.2003, a number of decompiling/obfuscating tools can be found at the Microsoft web site. For Java, a great number of commercial or free Java decompilers and obfuscators can be found by running a simple search on the Web.

Finally, OS-level protection mechanisms need to be utilized, along with the platform ones, in order to ensure good protection of the stored data. All of the hard work at the platform level is useless if code or data can be obtained and inspected as raw binary files. Therefore, any normal OS operation security rules (like ACL, minimizing attack surface, the principle of "least privilege," etc.) should be employed in addition to the platform-specific ones, in order to ensure blanket protection.

Certificate Management

Before addressing cryptography-based code protection features, the issue of certificate management in general needs to be covered, because all cryptography-based solutions deal, in one way or another, with certificates or keys. First of all, certificates need to be created and stored, and then accessed from the applications. Both platforms supply tools to issue certificate requests, as well as APIs for accessing the stored certificates.

.NET, as usual, heavily relies on Windows certificate stores to deal with certificates — they are used to store X509 certificates and certificate chains of trusted signers. There are a number of tools included with the .NET SDK to help accessing those stores, manage certificates, and sign assemblies using those certificates.

.NET's Certificate API is represented in its System.Security.Cryptography.X509Certificates namespace, where the X509Certificate class is of particular interest to us. Unfortunately, this class is rather poorly designed; it does not support accessing certificates in certificate stores, but works only with binary ASN.1 DER format, and does not provide any way to use it in asymmetrical encryption. The official suggestion from Microsoft is to stick with using unmanaged CryptoAPI (CAPI) functions, in particular CryptExportKey/CryptImportKey. See MSDN articles for details of bridging .NET's managed certificate implementation with CAPI.

Another, much better alternative is using WSE (already covered in Part 2). It provides the Microsoft.Web.Services.Security.X509 namespace with several useful classes, among them another version of X509Certificate, derived from the .NET-supplied one. This class recognizes the shortcomings of its predecessor and provides a very convenient interface for accessing certificate stores, as well as extracting public/private key information in a format appropriate for asymmetric encryption. As an added benefit, it can read certificates stored in Base64 text format. Together with the X509CertificateStore class, they make .NET's certificate API pretty well rounded. The following MSDN example shows how they can be used together:

// Open and read the Personal certificate store for 
// the local machine account.
X509CertificateStore myStore = 
  X509CertificateStore.LocalMachineStore(
  X509CertificateStore.MyStore);
myStore.OpenRead();

// Search for all certificates named "My Certificate" 
// add all matching certificates 
// to the certificate collection.
X509CertificateCollection myCerts = 
  myStore.FindCertificateBySubjectString(
          "My Certificate");
X509Certificate myCert = null;

// Find the first certificate in the collection 
// that matches the supplied name, if any.
if (myCerts.Count > 0)
{
  myCert = myCerts[0];
}

// Make sure that we have a certificate 
// that can be used for encryption.
if (myCert == null || 
    !myCert.SupportsDataEncryption)
{
  throw new ApplicationException(
    "Service is not able to encrypt the response");
  return null;
}

The Java platform implements RFC 3280 in the Certification Path API, which is supplied in the default "SUN" provider. This API, however, allows read-only — retrieving, accessing attributes, etc. — access to certificates, because they are considered to be immutable entities in Java. Classes implementing Certification Path API, belong to the JCA framework and can be found in the java.security.cert package. There are three classes of interest there:

  • Certificate: An abstract class for dealing with certificates.
  • X509Certificate: An abstract class for dealing specifically with X.509 certificates, stored using Base64 encoding, with BEGIN CERTIFICATE/END CERTIFICATE markers serving as delimiters.
  • CertificateFactory: A factory for generating certificate objects from their encoded formats.

Java uses so-called keystores for storing certificates. They can have different formats, as supplied by JCA providers (see Part 2); the default is Sun's proprietary JKS format. There are common keystores that contain keys, both public and private (or symmetric, if desired), and truststores, which are used to establish certificate chains. The JVM uses truststores (lib/security/cacert by default) to store the trusted certificates, and keystores for accessing key information. Having keystores as separate files is a nice feature in Java, as it is easy to move them around and manage them (compared to .NET's reliance on CAPI containers). Both stores can be specified as parameters on the command line, or accessed directly from code:

java -Djavax.net.ssl.keyStore=MyKeyStore 
    -Djavax.net.ssl.keyStorePassword=password 
    -Djavax.net.ssl.trustStore=MyTrust MyClass

Compared to the standard .NET certificate implementation, Java provides very convenient facilities of working with certificates. The examples below demonstrate how easy it is to obtain certificates from a keystore:

FileInputStream fin = new FileInputStream("MyKeyStore");
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(fin,"password");
Certificate cert = ks.getCertificate("MyEntry");

or from a file:

FileInputStream fin = 
            new FileInputStream("MyCert.cer");
CertificateFactory factory = 
            CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)            
            factory.generateCertificate(fin);

Java provides a tool for generating keys, keytool, which has a number of options. Among them are importing/exporting certificates, creating test certificates, creating certificate signing requests, etc. This tool picks up the keystore types (JKS, PKCS#12, etc.) defined in the java.security configuration file, and can use plugged-in provider types to operate on various types of keystores. By default, keytool generates only X.509 v1 certificates, which may be restricting for some applications.

Becoming a Certificate Authority (CA) on your own is problematic, but not impossible, with Java. One can either purchase a commercial library, or build his or her own CA using sun.security.x509 classes, although they only work with JKS keystores. However, the latter solution is neither portable nor documented. There are also a number of open source libraries that allow you to deal with certificate management, including CA functionality. A good free implementation is OpenSSL.

Note: Java provides a solid API for dealing with certificates. .NET programmers have to turn to unmanaged CAPI functions to access certificates, unless they use WSE, which adds a lot of useful functionality.

Pages: 1, 2, 3, 4

Next Pagearrow