Reads:37691Replies:0
Java security - provider-related architecture
Security provider
The attribute of Java as secure software is reflected in the concept of its security software package. In other words, common concepts in the security field including identification, encryption and signing are supported by Java through the security software package. Java defines the security software package as a set of abstract interfaces. Sun, creator of Java, provides a set of implementations. The security software package consists of security providers, algorithms and engines. An engine is a set of operations while an algorithm defines how to execute those operations. A security provider, however, is responsible for implementing the two abstract concepts. For example, a message digest is an engine, an operation that can be executed by developers. The idea of message digest has nothing to do with how message digest is calculated. All message digests share the same attributes, and the abstracted interface is the engine. The algorithm for implementing message digests can be MD5 or SHA and is implemented by specific classes. The security provider acts as a link between the engine and the algorithm and manages both of them. Security providers are designed to provide developers with a simple mechanism for easily changing or replacing existing algorithms and their implementations. Through the security provider, developers only need to use the engine interface, without concern for the specific class implementing the algorithm, or the specific security provider providing the algorithm. Architecture The architecture of the Java security software package system is comprised of four parts: Engine JVM provides the engine class, which is part of Java core APIs. Algorithm Each engine is implemented by a set of algorithms. Java provides a set of default algorithm implementations (provided by Sun) while third-party organizations can provide more implementations. Provider Algorithm classes are managed by a provider, and the provider manages the mappings between the algorithms and their implementations. Security class Security classes maintain a list of providers. By viewing security classes, you can learn about providers and their provided supported algorithms. The work flow of the security provider system is as follows: Service class > Engine: Invoke an interface. Engine > Security class: Query. Security class > Provider: Locate the provider. Provider > Algorithm: Locate appropriate algorithms. Algorithm > Service class: Return the calculation result. Using the getInstance() method for MessageDigest as an example, it is discovered that the Security.getImpl() method is called for implementation while the internal implementation is made by such statements: GetInstance.getInstance (type, getSpiClass(type), algorithm, params, provider).toArray(); In this case, getInstance returns an instance object whose class declaration is as follows: public static final class Instance { public final Provider provider; public final Object impl; private Instance(Provider arg0, Object arg1) { this.provider = arg0; this.impl = arg1; } public Object[] toArray() { return new Object[] {this.impl, this.provider}; } } From the statements above, we can see that it is actually an encapsulation of a provider and an object (actual algorithms). Provider selection When JVM starts, it registers providers in $JREHOME/lib/security/java.security. Using the files on my PC as an example: # # List of providers and their preference orders (see above): # security.provider.1=sun.security.provider.Sun security.provider.2=sun.security.rsa.SunRsaSign security.provider.3=sun.security.ec.SunEC security.provider.4=com.sun.net.ssl.internal.ssl.Provider security.provider.5=com.sun.crypto.provider.SunJCE security.provider.6=sun.security.jgss.SunProvider security.provider.7=com.sun.security.sasl.Provider security.provider.8=org.jcp.xml.dsig.internal.dom.XMLDSigRI security.provider.9=sun.security.smartcardio.SunPCSC security.provider.10=apple.security.AppleProvider JVM registers these providers during startup; namely Security reads the file during initialization. To develop custom security providers, you need to place classes under the path to system classes. By viewing the code of the Provider class, you can see that Provider is actually a Properties file, which stores engine names and the implementations of specific algorithm classes in KV pairs. In the following application example, you can view the actual provider engine and algorithms: package com.taobao.cd.security; import java.security.Provider; import java.security.Security; import java.util.Enumeration; public class ProviderTest { public static void main(String[] args) { // TODO Auto-generated method stub Provider[] providers = Security.getProviders(); for (int i = 0; i < providers.length; i++) { System.out.println("" + (i + 1) + ":" + providers); for (Enumeration e = providers.keys(); e.hasMoreElements();) { System.out.println("\t" + e.nextElement()); } } } } Part of the output is as follows: 1:SUN version 1.8 Alg.Alias.Signature.SHA1/DSA Alg.Alias.Signature.1.2.840.10040.4.3 Alg.Alias.Signature.DSS SecureRandom.SHA1PRNG ImplementedIn KeyStore.JKS Alg.Alias.MessageDigest.SHA-1 MessageDigest.SHA ... Architectural design of engine classes The architectural design of engine classes is worth noting. As mentioned above, engine classes provide interfaces for service developers and also for third-party providers. Each engine provides providers with an interface, namely the SPI (security provider interface). The following uses MessageDigest (the message digest engine) as an example. MessageDigest inherits MessageDigestSpi. MessageDigestSpi abstract classes define the operations to be executed by the message digest engine. MessageDigest internally has a Delegate class while the core method of MessageDigest is getInstance(), which is used to obtain the instances of a class. The actual implementation is as follows: public static MessageDigest getInstance(String algorithm, String provider) throws NoSuchAlgorithmException, NoSuchProviderException { if (provider == null || provider.length() == 0) throw new IllegalArgumentException("missing provider"); Object[] objs = Security.getImpl(algorithm, "MessageDigest", provider); if (objs[0] instanceof MessageDigest) { MessageDigest md = (MessageDigest)objs[0]; md.provider = (Provider)objs[1]; return md; } else { MessageDigest delegate = new Delegate((MessageDigestSpi)objs[0], algorithm); delegate.provider = (Provider)objs[1]; return delegate; } } By using Security.getImpl() to obtain corresponding providers and algorithm implementation classes and by executing type identification and the else delegate logic, a MessageDigest instance will be returned. The following uses the MD5 algorithm provided by Sun security as an example to explain it more specifically. Though MD5 does not directly implement MessageDigestSpi, its parent class DigestBase inherits MessageDigestSpi. Therefore, the algorithm still meets the design requirements of the architecture. DigestBase achieves the general implementation of message digests and provides three abstract interfaces: abstract void implCompress(byte[] arg0, int arg1); abstract void implDigest(byte[] arg0, int arg1); abstract void implReset(); Part of the specific implementation of the code in MD5 is as follows: void implDigest(byte[] arg0, int arg1) { long arg2 = this.bytesProcessed << 3; int arg4 = (int) this.bytesProcessed & 63; int arg5 = arg4 < 56 ? 56 - arg4 : 120 - arg4; this.engineUpdate(padding, 0, arg5); ByteArrayAccess.i2bLittle4((int) arg2, this.buffer, 56); ByteArrayAccess.i2bLittle4((int) (arg2 >>> 32), this.buffer, 60); this.implCompress(this.buffer, 0); ByteArrayAccess.i2bLittle(this.state, 0, arg0, arg1, 16); } The following class diagram shows the architecture of the class: ![]() |
|