The Java platform provides a way to plug in security providers that implement security algorithms. For example, the SunJCE provider supplies implementations of various algorithms such as the Digital Signature Algorithm (DSA), Key Agreement, and Secret-Key Factory algorithms.
In this article, we'll learn how to write our own security provider in Java and how to integrate it into the Java security architecture.
A security provider is a piece of software that supplies implementations of security algorithms. The Java platform comes with a default security provider, called the "SUN" provider (or the "SunJCE" provider), which supplies implementations of various algorithms such as the Digital Signature Algorithm (DSA), Key Agreement, and Secret-Key Factory algorithms.
However, the SUN provider is not the only security provider available. Other providers, such as the Bouncy Castle provider, also supply implementations of various security algorithms.
The Java security architecture is designed to allow applications to plug in different security providers as needed. This architecture is based on the following concepts:
For example, the KeyFactory service is responsible for converting keys (of type Key) into key specifications (of type KeySpec), and vice versa. The SUN provider supplies an implementation of this service, and the Bouncy Castle provider also supplies an implementation of this service.
The Java security architecture defines a number of standard service types, such as KeyFactory, KeyPairGenerator, and MessageDigest. A provider that supplies an implementation of one of these standard services is said to "implement" that service.
Before a provider can be used, it must be registered with the Java security architecture. This can be done programmatically, by calling the Security.addProvider() method, or statically, by adding the provider to the java.security file.
Adding a provider statically is the preferred approach, as it does not require modification of application code.
The following is an example of adding a provider statically:
security.provider.1=com.example.MyProvider
In this example, MyProvider is the name of the provider class.
Every provider must have a provider class. The provider class is a concrete subclass of the Provider class. The provider class must have a public constructor that takes a single String argument, which is the provider name.
The provider class must also override the getName() and getInfo() methods inherited from the Provider class.
The following is a simple example of a provider class:
package com.example;
import java.security.Provider;
public class MyProvider extends Provider {
public MyProvider() {
super("MyProvider", 1.0, "MyProvider");
}
@Override
public String getName() {
return "MyProvider";
}
@Override
public String getInfo() {
return "MyProvider 1.0";
}
}
In this example, the provider name is "MyProvider" and the provider version is "1.0".
Once a provider class has been written, the next step is to implement one or more services. As mentioned earlier, a service is a well-defined set of algorithms (or other functionality) that a provider may implement.
For example, the SUN provider supplies implementations of various services such as the KeyFactory service, the KeyPairGenerator service, and the MessageDigest service.
To implement a service, a provider must supply a concrete subclass of the Service class. This subclass must have a public constructor that takes the following arguments:
The following is a simple example of a Service subclass:
package com.example;
import java.security.Provider;
import java.security.Service;
public class MyService extends Service {
public MyService(Provider provider, String type, String algorithm, String className) {
super(provider, type, algorithm, className, null, null);
}
}
In this example, the type of the service is "KeyFactory", the algorithm name is "DSA", and the class name of the implementation is "com.example.MyKeyFactory".
The KeyFactory service is responsible for converting keys (of type Key) into key specifications (of type KeySpec), and vice versa.
A KeyFactory service must implement the following methods:
In addition, a KeyFactory service may optionally implement the following methods:
The KeyFactory service is typically used by applications to convert between different key formats. For example, an application may have a key in the form of an X.509 certificate and need to convert it into a format that can be used by the Signature service.
The following is a simple example of a KeyFactory service:
package com.example;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeySpec;
import java.security.spec.X509EncodedKeySpec;
public class MyKeyFactory extends KeyFactory {
public MyKeyFactory() {
super("MyKeyFactory");
}
@Override
public Key generatePublic(KeySpec keySpec) {
// TODO: Implement this method
return null;
}
@Override
public Key generatePrivate(KeySpec keySpec) {
// TODO: Implement this method
return null;
}
@Override
public KeySpec getKeySpec(Key key, Class keySpec) {
// TODO: Implement this method
return null;
}
@Override
public Key translateKey(Key key) {
// TODO: Implement this method
return null;
}
}
In this example, the KeyFactory service is named "MyKeyFactory" and it implements the generatePublic(), generatePrivate(), getKeySpec(), and translateKey() methods.
The Signature service is responsible for creating and verifying digital signatures.
A Signature service must implement the following methods:
In addition, a Signature service may optionally implement the following methods:
The Signature service is typically used by applications to create and verify digital signatures. For example, an application may use the Signature service to sign a document before sending it over the network.
The following is a simple example of a Signature service:
package com.example;
import java.security.Key;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
public class MySignature extends Signature {
public MySignature() {
super("MySignature");
}
@Override
public void initSign(PrivateKey privateKey) {
// TODO: Implement this method
}
@Override
public void initVerify(PublicKey publicKey) {
// TODO: Implement this method
}
@Override
public void sign() {
// TODO: Implement this method
}
@Override
public boolean verify(byte[] signature) {
// TODO: Implement this method
return false;
}
}
In this example, the Signature service is named "MySignature" and it implements the initSign(), initVerify(), sign(), and verify() methods.
The MessageDigest service is responsible for creating message digests. A message digest is a cryptographic hash function that is used to create a digital fingerprint of a piece of data.
A MessageDigest service must implement the following methods:
In addition, a MessageDigest service may optionally implement the following methods:
The MessageDigest service is typically used by applications to create message digests. For example, an application may use the MessageDigest service to create a message digest of a document before sending it over the network.
The following is a simple example of a MessageDigest service:
package com.example;
import java.security.MessageDigest;
public class MyMessageDigest extends MessageDigest {
public MyMessageDigest() {
super("MyMessageDigest");
}
@Override
public void update(byte[] data) {
// TODO: Implement this method
}
@Override
public void update(byte[] data, int off, int len) {
// TODO: Implement this method
}
@Override
public byte[] digest() {
// TODO: Implement this method
return null;
}
@Override
public int digest(byte[] data, int off, int len) {
// TODO: Implement this method
return 0;
}
@Override
public boolean isEqual(byte[] digestA, byte[] digestB) {
// TODO: Implement this method
return false;
}
@Override
public void reset() {
// TODO: Implement this method
}
@Override
public Object clone() {
// TODO: Implement this method
return null;
}
}
In this example, the MessageDigest service is named "MyMessageDigest" and it implements the update(), update(), digest(), digest(), isEqual(), reset(), and clone() methods.
In this article, we've learned how to write our own security provider in Java and how to integrate it into the Java security architecture. We've also looked at a few examples of services that a provider can implement, such as the KeyFactory service, the Signature service, and the MessageDigest service.