ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


Java vs. .NET Security, Part 2
Cryptography and Communication

by Denis Piliptchouk
12/10/2003

This is the second article in a series on Java vs. .NET security comparisons. It deals with the issues of cryptography support and the mechanisms of communication protection on those platforms. The previous article in this series, Part 1, covered configuration and code containment.

In today's world, most of the means of secure data and code storage and distribution rely on using cryptographic schemes, such as certificates or encryption keys. Thus, cryptography mechanisms form a foundation upon which many important aspects of a solid security system are built, and it is crucial for a really secure platform to provide adequate support for these services.

Once an application steps out of the bounds of a single-computer box, its external communication is immediately exposed to a multitude of outside observers with various intentions, their interests ranging from employers scanning the list of web sites an employee visits to business spies looking for a company's "know-how." In order to protect sensitive data while it is en route, applications invoke different methods, most often with some kind of cryptographic protection applied to the data before transmitting it. Any respectable enterprise system has to demonstrate adequate protection measures in this area.

O'Reilly Emerging Technology Conference.

Cryptography: General

Cryptography in .NET is based to a large extent on the Windows CryptoAPI (CAPI) service, with some extensions. Many algorithms are implemented as managed wrappers on top of CAPI, and the key management system is based on CAPI key containers. Most cryptography-related classes reside in the System.Security.Cryptography namespace, with certificate classes separated into X509Certificates and XML digital signature functionality into Xml subpackages. Web Service Extensions (WSE; see Secure Communication section) provides its own set of certificate classes in the Microsoft.Web.Services.Security.X509 package.

However, .NET's Cryptography service is more than just a managed wrapper -- it extends the CAPI in a number of ways. First, it is highly configurable and allows adding custom algorithm implementations in the machine.config file. Second, .NET uses a stream-based model, where all cryptographic transformations (except for asymmetric algorithms) are always performed on streams. Third, the defaults for all algorithms are configured to the strongest and safest settings (subject to Windows OS encryption settings, though), so the default objects that the user receives are most secure from what his Windows encryption settings allow.

The cryptography model of .NET is horizontally organized into several layers, and vertically grouped by types. Each family of algorithms (symmetric, asymmetric, etc.) forms a vertical hierarchy, deriving from a single root class for that family, with (usually) two more levels beneath it: an abstract algorithm level, and its concrete implementation. Family root classes are sealed; i.e. they cannot be extended by applications. This means, for instance, that the family of asymmetric algorithms can not be extended beyond the provided RSA and DSA abstractions. By .NET's convention, the implementation class is called Provider if it is a wrapper around a CAPI object, or Managed if it is a completely new implementation. The (simplified) System.Security.Cryptography class hierarchy is shown in Figure 1:


Figure 1. .NET cryptography class hierarchy

The Java platform's cryptography support has two parts to it: Java Cryptography Architecture (JCA) and Java Cryptography Extension (JCE), which were separated (due to US export restrictions) to gain exportability for the Java platform. All cryptography functions, which are subject to export laws, have been moved to JCE. In JDK 1.4, JCE became an internal part of the Java platform, instead of being an optional package, as it had been up to 1.4.

Both JCA and JCE have a similar provider-based architecture, which is widely employed in many of the Java platform's solutions. Those packages consist of so-called frameworks, which implement the required infrastructure, and a number of additional providers supply cryptography algorithms. JCA and JCE frameworks are internal Java packages, and cannot be replaced or bypassed. The JCE framework authenticates JCE providers, which should to be signed by a trusted Certificate Authority (Sun or IBM) -- see the JCE Provider Reference for details.

Note that prior to v1.4, JCE was an extension and its framework classes could be supplied by a third-party vendor along with the provider itself, so the problem with signing could be avoided by removing Sun's JCE 1.2.2 provider and framework from the configuration. Since JCE has become a standard Java package, this poses a problem for independent vendors, because the involved procedure is rather long and complicated. Thus, with J2SE v1.4, vendors are forced to undertake the signing procedure, or begin developing proprietary solutions and abandon the JCE framework -- see the JCE Reference Guide for further information.

The JCA Provider framework model, shown in Figure 2, consists of the following elements:


Figure 2. JCA class hierarchy

Note: Java requires its crypto providers to be signed by a trusted CA, which poses an obstacle for independent vendors.

Cryptography: Algorithms

Most common industry-standard cryptographic algorithms (Symmetric, Asymmetric, Hashes, Signatures, PBE) and stream/block ciphers are available out-of-the-box on both platforms.

The following families of algorithms are supplied in System.Security.Cryptography namespace of .NET:

These asymmetric algorithm helpers are used together with configured asymmetric providers to do their jobs:

The .NET Cryptography library provides Password Based Encryption (PBE) functionality through its PasswordDeriveBytes class. It uses the specified hashing algorithm to produce a secret key for the targeted symmetric encryption algorithm. A sample application that demonstrates symmetric encryption with PBE to encrypt/decrypt is available as a .zip file for download.

Symmetric and hash transforms in .NET are stream-based, so multiple transformations can be chained together without creating temporary buffer storage. The CryptoStream class derives from the System.IO.Stream, and plugs into any framework where stream interfaces are acceptable: memory, data, network, and other kinds of data. CryptoStream accepts the ICryptoTransform interface, which it then uses internally to transform the data block-by-block by calling TransformBlock repeatedly. This interface is implemented differently by symmetric and hash providers:

1. Using Streams with Symmetric Algorithms

In case of a symmetric algorithm, the top-level SymmetricAlgorithm class defines the abstract methods CreateEncryptor/Decryptor. These methods' implementations in derived classes (providers) create an instance of CryptoAPITransform class, appropriate for the particular algorithm, and return it to use with CryptoStream. The CryptoAPITransform class internally hooks to the CryptoAPI Windows service to do the job using the _AcquireCSP and _EncryptData private unmanaged functions, as shown in Figure 3:


Figure 3. Streams with .NET symmetric algorithms

2. Using Streams with Hash Algorithms

The HashAlgorithm family root class itself implements the ICryptoTransform interface, so any derived object can be used directly with CryptoStream. Its implementation of the TransformBlock method simply delegates the call to the derived class' implementation of the abstract method HashCore, as demonstrated in Figure 4:


Figure 4. Streams with .NET hash algorithms

In Java, the following services are defined in the JCA framework (java.security.* packages), and Sun supplies two JCA providers ("SUN" and "RSAJCA") with J2SE v1.4.2:

As explained before, JCE has been separated out due to export restrictions. Its framework classes reside in javax.crypto.* packages and the Sun-supplied default provider "SunJCE" is shipped with J2SE v1.4.2:

Additionally, Sun's provider supplies some JCA algorithms used by JCE: KeyPairGenerator; AlgorithmParameterGenerator for DH; AlgorithmParameters managers for DH, DES, 3DES, Blowfish, and PBE; and KeyStore implementation for "JCEKS".

This sample application demonstrates symmetric encryption and PBE to encrypt/decrypt a data file.

Surprisingly, however, many third-party providers (both commercial and free) provide a better selection of algorithms. For comparison, check the list of algorithms provided by an open source implementation from Bouncy Castle.

Note: Both platforms supply plenty of algorithms with default installations. There are quite a few independent Java vendors who offer even better selection than Sun's defaults.

Cryptography: Configuration

Cryptography systems on both platforms use configurable plug-in architectures -- new algorithms, or updated implementations of existing ones can be added without code changes, by changing just few properties in the system configuration files.

A distinct feature of .NET's symmetric, asymmetric, and hash crypto family hierarchies (see the Algorithms section) is their configurability -- all abstract classes in the hierarchies define static Create methods, allowing name-based lookup of the requested algorithm implementation in the machine.config file. New implementations may be mapped to existing (or new) names and will be picked up by the calls to the Create method, as explained later in this section. Classes of the Cryptography namespace that are not in those hierarchies do not follow this hierarchical approach and are not configurable by name.

At the heart of .NET Cryptography configuration lies the CryptoConfig utility class, which maps implementation classes to algorithm names, as configured in the machine.config file (or with the use of hardcoded defaults):

<cryptographySettings> 
 <cryptoNameMapping> 
  <cryptoClasses> 
   <cryptoClass MySHA1Hash="MySHA1HashClass, 
                MyAssembly Culture='en', 
                PublicKeyToken=a5d015c7d5a0b012, 
                Version=1.0.0.0"/> 
  </cryptoClasses> 
  <nameEntry 
     name="SHA1" class="MySHA1Hash"/> 
  <nameEntry 
     name="System.Security.Cryptography.SHA1"
     class="MySHA1Hash"/> 
  <nameEntry 
name="System.Security.Cryptography.HashAlgorithm"
     class="MySHA1Hash"/> 
 </cryptoNameMapping> 
 <oidMap> 
  <oidEntry OID="1.3.14.33.42.46" name="SHA1"/> 
 </oidMap> 
</cryptographySettings>

Application developers have the following choices when creating a configurable algorithm object:

Continuing with the previous configuration example:

//all calls return an instance of MySHA1HashClass

HashAlgorithm sha1 = 
  System.Security.Cryptography.SHA1.Create();

HashAlgorithm sha1 = 
  System.Security.CryptoConfig.CreateFromName("SHA1");

HashAlgorithm sha1 = 
  System.Security.Cryptography.HashAlgorithm.Create();

Configuration's nameEntry tags form a lookup table, which is consulted when CryptoConfig.CreateFromName is called. Any string can be used as a name, as long as it is unique (see "Specifying Fully Qualified Type Names" in the MSDN documentation for character restrictions). The OID mapping is optional; it allows mapping ASN.1 Object Identifiers to an algorithm implementation. If no algorithm-name configuration is specified, the following defaults are used. Note the following strong defaults for algorithm families:

In order to be usable after having been installed, Java's JCE providers should be made known to the runtime system. A Provider can be configured either declaratively in the java.security file:

// adding a crypto provider at the third position
security.provider.3=com.MyCompany.ProviderClassName

or programmatically by the code at runtime:

// appending a provider to the list
Security.addProvider(
          new com.MyCompany.ProviderClassName());
// adding a crypto provider at the third position
Security.insertProviderAt(
        new com.MyCompany.ProviderClassName(),3);

Programmatic runtime configuration assumes that the necessary permissions are granted to the executing code by the security policy (note that the providers themselves may require additional permissions to be specified):

// java.policy
// granting permissions for programmatic configuration
grant codeBase "file:/home/mydir/*" {
	permission java.security.SecurityPermission 
                        "Security.setProperty.*";
	permission java.security.SecurityPermission 
                     "Security.insertProvider.*";
	permission java.security.SecurityPermission 
                     "Security.removeProvider.*";
}

Whether they were added declaratively or programmatically, all Providers end up in a single list and are queried for the requested algorithms (with optional parameters like mode and padding) according to their positions in the list (one being the highest) until finding a match. This process is shown in Figure 5. Algorithm and parameter names are hardcoded inside of the providers and cannot be changed. Developers can optionally request using only a particular provider, when they create an instance of an algorithm. This can be used, for example, when the developers want to use only particularly certified providers (for instance, DoD):

//requesting an implementation 
//from only a single provider
Signature sigAlg1 = Signature.getInstance(
                "SHA1withDSA","MyGreatProvider");

//requesting the first matching implementation 
Signature sigAlg2 = Signature.getInstance(
                "SHA1withDSA");

Figure 5. JCA collaborations

Note: Overall, both platforms are pretty even when it comes to configurability. Defaults for algorithm names are a convenient feature in .NET. Java, on the other hand, allows specifying additional algorithm details besides the name.

Secure Communication

During transmission, data can be protected on three levels: hardware, platform, and application. These can be used independently, or combined for better results. In all cases, there is some kind of cryptographic protection applied to the data prior to communication, but the amount of required application code and its complexity increases, with application-level solution being the most involved. While wire-level protocols (IPSec, for instance) may be implemented at the hardware level for speed and efficiency, they are not discussed in this article in order to keep the primary focus on the platforms themselves.

At the platform level, SSL is the de facto industry standard of transport protection. Both platforms support (to some extent) the latest SSL 3.0 specification that allows mutual authentication of both client and server. Recently, TLS 1.0 specifications were released by IETF (RFC 2246) as a new standard for Internet communication security, which is supposed to gradually replace SSL.

Additionally, both platforms expose -- albeit at different levels -- implementations of the Generic Security Service API (GSSAPI) (RFC 1508, 1509) common standard, which defines a generic programming interface for different authentication and communication protocols.

Secure Communication: Platform

Windows OS implements GSSAPI in the so-called Security Support Provider Interface (SSPI) to select one of the configured providers for securing data exchange over networked connections, which is used internally by .NET itself. However, as ridiculous as it sounds, .NET applications have only SSL configuration in IIS at their disposal for protection of HTTP-based traffic, while non-IIS based applications, such as standalone Remoting (the successor to DCOM) or HTTP servers, have no means of protecting their data en route. Not surprisingly, during the first year after .NET 1.0's release, protection of Remoting communication was one of the most frequently asked questions on the web forums.

There still exists no officially supported solution for securing Remoting communication, but fortunately, its highly flexible sink architecture allowed for the development of a number of low-level solutions that can be plugged into the infrastructure and server as a substitute for platform-level protection. Microsoft also apparently realized its omission, and released a fix in the form of two assemblies in the samples namespace, Microsoft.Samples: Security.SSPI and Runtime.Remoting.Security. The former exposes a managed SSPI wrapper, and the latter uses it to implement a Remoting sink featuring symmetric encryption. Another article, which appeared at MSDN Magazine, outlined an alternative approach to Remoting security using asymmetric encryption.

The Java platform offers Java Secure Socket Extensions (JSSE) as a platform-level service for securing TCP/IP-based communication in vanilla J2SE applications, and J2EE's servlet specifications declare options for configuring SSL protection and refusing unprotected connection attempts.

Additionally, application servers from various vendors usually include some means to configure the SSL protocol for their HTTP servers. Since these are proprietary solutions, they are not going to be further pursued in this document.

JSSE, originally an extension to J2SE, was incorporated as a standard package as of version 1.4, so any Java application may count on using its services. The standard JSSE API is located in the javax.net.* packages (javax.security.cert is obsolete and should not be used). It is quite rich; readers should consult the Javadocs for the specified packages and the online documentation for the class model and operation overview.

The example below shows a simple scenario of a client/server application, which will be satisfactory in most cases. Normal sockets are replaced with SSL ones by specifying different factory implementations, which are consequently used to obtain input/output streams:

//client establishing a connection
SSLSocketFactory clientFactory = 
 (SSLSocketFactory)SSLSocketFactory.getDefault();
SSLSocket sslSocket = (SSLSocket)
clientFactory.createSocket(host,port);

//use as a normal socket
OutputStream out = sslSocket.getOutputStream();
InputStream in = sslSocket.getInputStream();

...

//server accepting a connection, 
//requesting mutual authentication
SSLServerSocketFactory serverFactory = 
  (SSLServerSocketFactory)
   SSLServerSocketFactory.getDefault();
SSLServerSocket ss = (SSLServerSocket)
serverFactory.createServerSocket(port);
ss.setNeedClientAuth(true);

//use as a normal socket
SSLSocket socket = ss.accept();
OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream();

...

A connection between two peers in JSSE is represented by a javax.net.ssl.SSLSession object. Among other things, this session contains negotiated shared secrets and information about ciphers used in the session. The master shared secret keys are not exposed through the JSSE API, and remain known only to the underlying implementation classes. Cipher information, however, is available for analysis, and the server may refuse a connection if the client does not use strong enough ciphers:

SSLSocket socket = ss.accept();
SSLSession session = socket.getSession();
String cipher = session.getCipherSuite();
if (cipher.equals("SSL_RSA_WITH_RC4_128_SHA") ||
cipher.equals("SSL_RSA_WITH_RC4_128_MD5")) {
	//sufficient strength, may continue
	...
} else {
	throw new SSLException(
            "Insufficient cipher strength!");
}

JSSE providers follow general JCE guidelines, and are pluggable into the provider-based JCA architecture. As a result, they may be configured in the java.security file, or added in code just like other security providers. Consequently, if a JCE provider, implementing the same algorithms as a JSSE one, is configured higher (i.e. it has a lower ordinal number -- see JCE Providers Configuration) in the crypto providers list than the JSSE provider, JSSE operations will use the JCE provider's implementations instead of the built-in ones. Note, however, that the JSSE cryptography algorithm implementations are private and not available for public usage. Also, as a departure from the usual provider model due to export restrictions, the default SSLSocketFactory and SSLServerSocketFactory cannot be replaced.

In JDK 1.4.2, Sun provides a reasonably good reference JSSE implementation named "SunJSSE," whose features are highlighted below. For the complete list, check the JSSE guide.

The following properties (all starting with javax.net.ssl) may be specified on the command line or set in code: keyStore, keyStorePassword, keyStoreType, trustStore, trustStorePassword, and trustStoreType. For example:

java -Djavax.net.ssl.trustStore=AppTrustStore SecureApp

Java applications using RMI communication are pretty much limited to using JSSE for protection. Sun is working on separate specifications for secure RMI, which will include authentication, confidentiality, or integrity mechanisms, but they are not going to be available any time soon -- the JSR 76 "RMI Security for J2SE" was rejected in February 2001. Custom solutions are possible (such as subclassing SSLServerSocket), but they are non-trivial.

The J2EE specification promotes usage of SSL/TLS across all of its components by mandating support for the following ciphers:

In most cases, cipher suites will be either SSL_RSA_WITH_RC4_128_SHA or SSL_RSA_WITH_RC4_128_MD5 (or their TLS equivalents), as they are currently the strongest commonly used SSL ciphers.

The servlet specification defines the transport-guarantee element in the deployment descriptor, which is used to require a certain level of call protection from the servlet container. Possible values are: NONE, INTEGRAL, and CONFIDENTIAL, and can be specified in the /WEB-INF/web.xml file. The names for the constraints are pretty self-descriptive, and implementation interpretation is left at the vendor's discretion. However, the servlets have an option to programmatically reject a HTTP connection if the HttpRequest.isSecure method shows that the connection is not secure. Below is an example of specifying the transport guarantee element:

<security-constraint>
  <user-data-constraint>
    <transport-guarantee>
      CONFIDENTIAL
    </transport-guarantee>
  </user-data-constraint>
</security-constraint>

EJBs do not have an option to determine connection's security settings. EJB specifications, however, require passive support for remote calls' security; i.e., if a call is made using a secure connection, EJB server also uses a secure connection for further calls. Remote EJB calls use the IIOP protocol, with support for SSL 3.0 and TLS 1.0 support mandated by EJB 2.0 and J2EE specifications.

Note: Besides IIS, .NET does not offer any standard means for communication protection at the platform level, while Java has a complete solution in this space.

Secure Communication: Application

For finer control over applied security mechanisms, an application can use an application-level, token-based protection mechanism, abstract from the underlying transmission protocol. This approach has an advantage over channel blanket encryption by being smarter and protecting only sensitive data. For instance, web services (see later in this section) use this paradigm for message protection, where only particular details of messages are signed and encrypted.

As already explained, J2SE includes GSSAPI, which may be utilized on the application level to provide token-based protection using the Kerberos V 5. GSSAPI framework, is quite a thin wrapper, delegating all requests to the underlying mechanism providers. The Java GSS mechanisms do not perform user logins themselves -- they should be done using JAAS prior to invoking GSSAPI services, and the credentials should be stored in some cache accessible to the GSS mechanism provider. Using JAAS and Kerberos tickets in GSS provides not only transport-level security protection, but also a principal delegation mechanism over the network. See "Authentication" in Part 4 for more information about JAAS and delegation.

GSS classes reside in the org.ietf.jgss package; check the online documentation for details and code examples.

Overall, Java offers a choice of platform-level (JSSE) and application-level (GSS) protection services with similar security goals: client-server authentication and protection of transmitted data. Listed below are a few criteria that can help to decide which service is more appropriate for a particular application:

Web services security specifications and toolkits also look at protecting individual messages, or tokens. This area has been rapidly evolving, and has not yet been fully standardized. Because of this lack of standards, both platforms provide only partial support for it via extensions or external products. However, since the topic of web services security alone warrants a whole separate book, it is not addressed here in any significant detail. Of all competing standards in this area, only SAML and WS-Security have been so far accepted for standardization by OASIS, with the latter still undergoing committee reviews.

For web services security, Microsoft is actively promoting its Web Service Architecture (WSA, formerly GXA), and adds support for all released up-to-date specifications via its Web Services Extension (WSE) pack for .NET. WSE is currently at 1.0 release, with 2.0 coming soon -- check the MSDN documentation for updates and new releases. Notably, WSE (and .NET in general) lacks support for SAML, even though the WS-Security specification does define binding for SAML assertions as one of the supported token types. In other areas, WSE provides relatively complete support of WS-Security and a number of other specifications. Additionally, WSE's certificate classes (located in the Microsoft.Web.Services.Security.X509 package) are much more convenient to deal with than .NET's original ones. The code sample below shows how to sign a request using WSE:

// Get SOAP context from the Web service proxy
SoapContext reqCxt = 
               serviceProxy.RequestSoapContext;

// Retrieve the certificate to be used for signing
Microsoft.Web.Services.Security.X509.X509Certificate crt = ...;
// Create a X509 security token
X509SecurityToken token = 
                     new X509SecurityToken(crt);

// Sign the request by adding 
//a signature to the request
reqCxt.Security.Tokens.Add(token);
reqCxt.Security.Elements.Add(
                       new Signature(token));

// Use the signed request to call the service...
serviceProxy.Hello();

The extensible architecture of .NET's Remoting has allowed for the development of quite interesting approaches to transport security, whereas Remoting's RPC-style invocations are transmitted and protected by the means of SOAP-based web service messages. In principle, this is not very different from the Microsoft solution described earlier, but it allows applying WSA-family protection (in particular, WS-Security) to individual messages, which ensures standard-based authentication, integrity, and authorization at the message level, as opposed to the non-standard approach of blank encryption of the former solution. For explanations and code samples, read the excellent publications at the CodeProject web site, in particular ""Remoting over Internet"" and related articles.

The Java platform does not provide direct support for web services security yet. Currently, there are two web services security-related JSRs at work: JSR 155 "Web Services Security Assertions", and JSR 183 "Web Services Message Security APIs". When accepted (although they have been in review stage for over a year now), these specifications should provide assertions support and transport-level security to web services written in Java. Although not a standard part of Java platform, IBM's Emerging Technologies Toolkit v1.2 (ETTK), formerly known as Web Services Development Kit, or WSTK, adds support for the current draft of WS-Security and some other specifications from the WSA family, of which IBM is a co-author.

Note: The .NET platform stays very current with the latest developments in web services security, while their support in Java is not standardized and is limited to offerings from individual vendors.

Conclusions

In this article, cryptography and communication protection on Java and .NET platforms were reviewed. Both platforms come out pretty even in terms of cryptographic features, although Java has a more complicated solution due to the obsolete US export restrictions. The picture becomes muddier when it comes to communication protection -- while Java fares much better by providing a choice of both platform and application-level solutions, it clearly lags behind .NET when it comes to support for web services security. Here, Java developers would have to turn to independent vendors for the desired features.

The next article of this series will cover code protection and Code Access Security (CAS) on both platforms.

Denis Piliptchouk is a senior technical member of BEA's AquaLogic Enterprise Security group, participates in OASIS WSS and WS-I BSP standards committees, and regularly contributes to industry publications.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.