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

advertisement

AddThis Social Bookmark Button

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:

  • Service (or Engine) abstract classes define types of functions available to developers, independent of particular algorithms: Asymmetric, Symmetric algorithms, Digests, etc.
  • Service Providers Interfaces (SPI) for each of those services link the high-level abstract Services to the provided implementations.
  • Provider is the central class that registers available implementations with the framework.
  • Security is the class that handles all providers.


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:

  • AsymmetricAlgorithm: digital signatures and key exchange functionality is also implemented by these family's providers (DSA,RSA).
  • HashAlgorithm: KeyedHashAlgorithm, MD5, multiple SHA.
  • SymmetricAlgorithm (DES, 3DES, RC2, Rijndael): additional parameters are specified by PaddingMode and CipherMode enumerations.
  • RandomNumberGenerator.

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

  • AsymmetricKeyExchangeFormatter/Deformatter: provides secure key exchange mechanism using OAEP or PKCS#1 masks.
  • AsymmetricSignatureFormatter/Deformatter: creates/verifies PKCS#1 v1.5 digital signatures, using any configured by name hash algorithm.

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:

  • MessageDigest: data hashing algorithms (MD5, SHA-1).
  • Signature: data signing and signature verification (DSA, RSAwithSHA1, RSAwithMD5).
  • KeyPairGenerator: generation of public/private pair of keys for algorithms (DSA, RSA).
  • KeyFactory: key conversions (DSA, RSA).
  • KeyStore: managing keystores (JKS).
  • CertificateFactory: certificate creation and CRL management (X.509).
  • AlgorithmParameters: algorithms' parameter management, including their encoding (DSA).
  • AlgorithmParameterGenerator: algorithms' parameter creation (DSA).
  • SecureRandom: random numbers generators (SHA1PRNG).

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:

  • Cipher: objects carrying out encryption/decryption according to an algorithm, mode, or padding (AES, DES, 3DES, Blowfish, PBE). Java ciphers have the additional functionality of wrapping/unwrapping secret keys to make them suitable for transfer or storage. The implementation and algorithm varies by provider (this can be a PBE, for instance).
  • CipherStream: combining input/output streams with a Cipher (CipherInputStream, CipherOutputStream).
  • KeyGenerator: generating keys for symmetric algorithms and HMAC.
  • SecretKeyFactory: conversions between key representations (AES, DES, 3DES, PBE).
  • SealedObject: protecting a serialized object's confidentiality with a cryptographic algorithm.
  • KeyAgreement: implementing Diffie-Hellman key agreement protocol (DH).
  • MAC: producing cryptographically secured digests with secret keys (HMAC-MD5, HMAC-SHA1, PBE).

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:

  • Invoke the new operator on the specific implementation class. This approach completely bypasses the .NET configuration mechanism.
  • Call the CryptoConfig.CreateFromName method to map an abstract name to a specific algorithm implementation class.
  • Using the factory pattern, call an overloaded static Create method on one of the abstract classes in the algorithm's family hierarchy (family root, or algorithm abstraction). Both overloads of Create will end up calling CryptoConfig.CreateFromName to retrieve the implementation class.

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:

  • System.Security.Cryptography.HashAlgorithm: SHA1CryptoServiceProvider
  • System.Security.Cryptography.AsymmetricAlgorithm: RSACryptoServiceProvider
  • System.Security.Cryptography.SymmetricAlgorithm: TripleDESCryptoServiceProvider

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.

Pages: 1, 2

Next Pagearrow