1. Trang chủ >
  2. Công Nghệ Thông Tin >
  3. An ninh - Bảo mật >

Chapter 10. Security APIs in Java 2

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (5.59 MB, 702 trang )


10.1.2 Guard Interface and GuardedObject Class

The Guard interface is provided to create an object used to guard a protected

resource. The supplier of the resource can create an object representing the

resource, encapsulate it into a GuardedObject, and keep the resource inside

this GuardedObject. In creating the GuardedObject, the supplier also

specifies the Guard object. The consumer of the resource can access the

resource object only if the security checks inside the Guard object are

satisfied.

The relationship between the GuardedObject, the Object and the Guard can

be seen in the constructor of the GuardedObject class:

public GuardedObject(Object object, Guard guard)



The only method in the Guard interface is called checkGuard(). It takes an

Object as its argument and it performs security checks to determine whether

or not to allow access to that object.

The Permission class in java.security implements the Guard interface. For

example, suppose a system thread is asked to open a file D:\itso\redbook.lwp

for read access, but the system thread does not know who the requester is or

under what circumstances the request is being made. Therefore, the system

thread can use the GuardedObject class to delay the access control

checking, as follows:

FileInputStream fis = new FileInputStream("D:\\itso\\redbook.lwp");

FilePermission fperm = new FilePermission("D:\\itso\\redbook.lwp", "read");

GuardedObject guardFile = new GuardedObject(fis, fperm);



Now the system thread can pass the guardFile object to the consumer thread.

For that thread to obtain the file input stream, it must call:

FileInputStream finps = (FileInputStream) guardFile.getObject();



The getObject() method in turn invokes the checkGuard() method on the

Guard object fperm, and because fperm is a Permission, its checkGuard()

method is:



public void checkGuard(Object object) throws SecurityException

{

SecurityManager sm = System.getSecurityManager();

if (sm != null)

sm.checkPermission(this);

}



298



Java 2 Network Security



This ensures that a proper access control check takes place within the

consumer context.



10.1.3 Providers

The java.security package also supplies a Provider class. The term

cryptographic service provider (provider for short) is used to refer to a

package or set of packages that supply a concrete implementation of a

subset of the cryptography aspects of the Java security API. The Provider

class is the interface to such a package or set of packages.

As we will see in 13.3.1, “The Provider Concept in the JCA” on page 485, for

each engine1 class in the API, a particular implementation is requested and

instantiated by calling a getInstance() method on the engine class, specifying

the name of the desired algorithm and, optionally, the name of the provider

whose implementation is desired. If no provider is specified, getInstance()

searches the registered providers for an implementation of the requested

cryptographic service associated with the named algorithm. In any Java

Virtual Machine (JVM), providers are installed in a given preference order

specified in the java.security file. That order is the order in which they are

searched when no specific provider is requested. If the implementation is

found in the first provider, it is used. If it is not found, it is searched for in the

second provider and so on. If it is not found in any provider, an exception is

raised. The getInstance() methods that include a Provider argument enable

developers to specify which provider they want an algorithm from. A program

can also obtain a list of all the installed Providers using the getProviders()

method in the Security class and choose one from the list.

Each provider class instance has a (currently case-sensitive) name, a version

number and a string description of the provider and its services. These three

pieces of information can be obtained by calling the methods getName(),

getVersion() and getInfo(), respectively.

10.1.3.1 Installing and Configuring Providers

Providers can be installed by first copying the package in the system and then

configuring the provider itself:

1. To install the provider classes, you can simply place the JAR file(s)

containing the classes anywhere on the user class path (see 3.4.3,

“Application Class Path” on page 88) or even on the boot class path (see

3.4.1, “Boot Class Path” on page 84). However, the best solution is to

supply the provider library as an installed or bundled extension, by placing

1

Engine is a term used to depict an abstract representation of a cryptographic service without a concrete

implementation.



Security APIs in Java 2



299



the JAR file(s) in the extensions directory, as explained in 3.4.2,

“Extensions Framework” on page 86.

For example, to install JCE 1.2 on your Java 2 SDK, Standard Edition,

V1.2 system, you can copy the JAR file jce1_2-do.jar in the extensions

directory. This directory is indicated as the value of the java.ext.dirs

system variable (see Appendix A, “Getting Internal System Properties” on

page 641).

Following these directions, you will be able to run all the programs that use

this particular provider. If you also need to develop and compile programs

using this provider, then the JAR file containing the provider classes must

be also copied in the extensions directory under the JRE development

directory (see 8.1, “A Note on java.home and the JRE Installation

Directory” on page 225).

2. Next, you need to configure the provider. For this you simply need to add it

to your list of approved providers.

• This is done statically by adding the provider to the security provider list

in the java.security file (see 8.3, “The Security Properties File,

java.security” on page 234).

For example, to configure JCE 1.2 on your Java 2 SDK, Standard

Edition, V1.2 system, the security provider called SunJCE must be

provided together with the SUN provider in the java.security file, as

shown:



security.provider.1=sun.security.provider.Sun

security.provider.2=com.sun.crypto.provider.SunJCE



As we have mentioned, the order number with which the provider is

added to the list is very important, in that if an implementation is

supplied in multiple providers, the implementation of the provider with

the higher preference (corresponding to the lower order number) is

chosen by the JVM.

In the same way, a provider is removed by simply deleting the entry

corresponding to it in the java.security file.

• Providers may also be registered dynamically. To do so, call either the

addProvider() or insertProviderAt() static method in the

java.security.Security class.

For example, to add the JCE 1.2 provider SunJCE dynamically, you can

use the following two lines of code:



300



Java 2 Network Security



Provider sunJce = new com.sun.crypto.provider.SunJCE();

int pos = Security.addProvider(sunJce);



The addProvider() method adds a new provider at the end of the list of

the installed providers.

On the other hand, the insertProviderAt() method adds a new provider

at a specified position in the array of providers. If the given provider is

installed in the requested position, the provider that used to be at that

position, and all the providers with a position greater than that, are

shifted up one position, toward the end of the list of the installed

providers.

Both the methods return the preference position in which the provider

was added, or -1 if the provider was not added because it was already

installed.

If the preference position of a provider has to be changed, the provider

must be first removed, and then inserted in back at the new preference

position.

A provider can be removed by calling the removeProvider() method of

the java.security.Security class.

Notice that the dynamic provider registration is not persistent and can

only be done by trusted programs or, in other words, programs that

have been granted the necessary permissions:

• To add a provider, or insert it in a specified position in the list, the

permission required is:

permission java.security.SecurityPermission "insertProvider.name"



• To remove a provider, the permission required is:

permission java.security.SecurityPermission "removeProvider.name"



Note that the SunJCE provider relies on some of the algorithm

implementations supplied by the SUN provider, which is the default provider

of the Java 2 SDK platform. This means that when you install the SunJCE

provider, you need to make sure that the SUN provider is also installed. We

will see more details on this in Chapter 13, “Cryptography in Java 2” on page

475.



10.1.4 The Security Class

As we mentioned in 10.1.3, “Providers” on page 299, the package

java.security also provides a Security class to manage installed providers and



Security APIs in Java 2



301



security-related properties. It only contains static methods and is never

instantiated. Its methods fall into two categories:

1. Methods used to get the installed providers, and also to add, delete, and

insert providers

• The method getProviders() can be used to get the list of all installed

providers. They are returned in a Provider array in the order of their

preference.

• The method getProvider() returns the Provider object specified in the

argument.

• The method addProvider() is used to add a provider to the end of the

list of installed providers, as shown in Step 2 on page 300. These

methods returns the preference position in which the provider was

added, or -1 if the provider was not added because it was already

installed.

• The method insertProviderAt() is used to add a new provider at a

specified position. This method returns the actual preference position

in which the Provider was added, or -1 if the provider was not added

because it was already installed, as shown in Step 2 on page 300. You

cannot install a provider that is already installed. If you need to change

the preference order, you must first remove the provider and then insert

it in the specified position.

• To remove a provider, use the removeProvider() method (see Step 2 on

page 300).

2. Methods used to get and set system-wide properties

The Security class also has methods to manage security properties. A

security property is accessible with the getProperty() method and can be

set with the setProperty() method. However, only trusted programs, or

programs with specific permissions, can use these methods.

The following program shows how to get the information about a provider:



import java.security.*;

class ProviderInformation

{

public static void main(String[] args)

{

String providername;

Figure 128. (Part 1 of 2). ProviderInformation.java



302



Java 2 Network Security



try

{

providername = args[0];

Provider myprov = Security.getProvider(providername);

if (myprov != null)

{

String info = myprov.getInfo();

System.out.println("\n\n" + info + "\n\n");

}

else

System.out.println("No provider with the speicified name is installed");

}

catch (Exception e)

{

System.out.println("There was an exception. The exception was " +

e.toString());

}

}

}

Figure 129. (Part 2 of 2). ProviderInformation.java



Compile this program with the command:

javac ProviderInformation.java



Then you can run it and pass it a provider name on the command line. By

default, the only security provider that comes with the installation of Java 2

SDK, Standard Edition, V1.2 is SUN, the provider supplied by Sun

Microsystems (see 8.3, “The Security Properties File, java.security” on page

234). By launching the command:

java ProviderInformation SUN



you would see the following output:

SUN (DSA key/parameter generation; DSA signing; SHA-1, MD5 digests;

SecureRandom; X.509 certificates; JKS keystore)



The information above shows the features of the SUN provider, listed in 8.3,

“The Security Properties File, java.security” on page 234.

Assuming that you have installed JCE 1.2 on your Java 2 SDK, Standard

Edition, V1.2 system, as indicated in Step 1 on page 299 and Step 2 on page



Security APIs in Java 2



303



300, you can invoke the above program and pass the SunJCE provider name

on the command line, as shown:

java ProviderInformation SunJCE



The output in this case is:

SunJCE Provider (implements DES, Triple DES, Blowfish, PBE, Diffie-Hellman,

HMAC-MD5, HMAC-SHA1)



The information above shows the features of the SunJCE provider, listed in

Point 2 on page 492.



10.1.5 Access Control APIs

The java.security package provides the AccessController class used to make

access control decisions based on the security policy in effect. It is also used

to mark code in the execution stack as privileged (see 3.2.3.1, “Lexical

Scoping of Privilege Modifications” on page 76), thus affecting subsequent

access determinations. Finally, this class is used to obtain a snapshot of the

current calling context, so that access control decisions from a different

context can be made with respect to the saved context.

A thread's security context is based upon the classes on its execution stack.

Each class is associated with a single protection domain which, in turn,

specifies the permissions granted to that class (see 3.3, “Java 2 Protection

Domain and Permissions Model” on page 80). So a security context can be

thought of as a stack of protection domains corresponding to the classes on

the stack. When an access control decision is made, this stack of protection

domains is examined; only if every protection domain possesses the

necessary permission does the access control check pass. If a protection

domain is examined that does not possess the necessary permission, an

AccessControlException is thrown.

Consider the following two lines of code:



FilePermission perm = new FilePermission("file", "read");

AccessController.checkPermission(perm);



This fragment of code checks whether the calling thread has permission to

read the given file. That is, each class on this thread's execution stack must

belong to a protection domain that includes the requested permission.

Assume that this thread creates a new thread. This new child thread has a

new stack with a new security context. If the parent's security context was not



304



Java 2 Network Security



retained, then security decisions made in the child thread would be based

solely on the child’s security context. This would enable less trusted code (in

the parent) to access protected resources by calling more trusted code (in the

child). To prevent this, Java ensures that a child thread automatically inherits

its parent’s security context. This inheritance continues down a

thread-creation hierarchy, so that whenever a resource access is attempted,

the security context of the executing thread, and those of all its ancestors,

must permit the access.

Typically, each class in a thread’s security context must possess the

requested permission in order for an access check to pass. However, this

top-to-bottom stack crawl can be short-circuited by using the

AccessController.doPrivileged() method to mark a class on the stack as

privileged. Once a privileged frame is encountered, no further protection

domains are examined. This means that code that would otherwise lack

permission to access a resource may do so if it calls (directly or indirectly)

code that possesses the permission and calls doPrivileged(). Note that if the

class that calls doPrivileged() does not possess the requested permission, an

AccessControlException is thrown as usual. The doPrivileged() method

enables trusted code to perform operations on behalf of callers that may or

may not have the necessary permission themselves.

An implementation of the PrivilegedAction interface is passed as an argument

to doPrivileged() to define the operation to be performed with privileges

enabled. This interface is used only for operations that do not throw checked

exceptions; operations that throw checked exceptions must be implemented

as a PrivilegedExceptionAction instead.

The AccessControlContext class in java.security is used to make access

control decisions based on the security context defined by an

AccessControlContext object. An AccessControlContext object is created by

calling AccessController.getContext(), which takes a snapshot of the current

calling context and places it in the AccessControlContext returned. The

AccessControlContext.checkPermission() method makes access control

decisions based upon the encapsulated context, rather than the context of the

current execution thread.



10.1.6 Key Management

The package java.security offers several interfaces and classes to provide

key generation and management.

• The Key interface is the top-level interface for all cryptographic keys and

defines the functionality shared by all keys. All keys have three

characteristics:



Security APIs in Java 2



305



1. An algorithm

This is the key algorithm for that key. The key algorithm is usually an

encryption or asymmetric operation algorithm (such as DSA or RSA).

The name of the algorithm of a key is obtained using the getAlgorithm()

method.

2. An encoded form

This is an external encoded form for the key used when a standard

representation of the key is needed outside the JVM, as when

transmitting the key to some other party. The key is encoded according

to a standard format (such as X.509 or PKCS#8), and is returned using

the getEncoded() method.

3. A format

This is the name of the format of the encoded key. It is returned by the

getFormat() method.

• PrivateKey and PublicKey are interfaces that extend the Key. These

interfaces contain no methods or constants. They merely serve to group

(and provide type safety for) all public and private key interfaces. The

specialized public and private key interfaces, such as the DSAPublicKey

and DSAPrivateKey interfaces in the java.security.interfaces package,

extend PublicKey and PrivateKey respectively.

• The java.security package also contains classes to manage keys and key

pairs, such as the KeyFactory class, which is used to convert keys into key

specifications (and vice versa), and the KeyFactorySpi class, which is

used to define the Service Provider Interface (SPI) for the KeyFactory

class. As we will see in 10.2, “The Package java.security.spec” on page

322, a representation of key material is opaque if it does not give you any

direct access to the key material fields.

Key factories are bi-directional. This means that they allow you to build an

opaque key object from given key material (specification), or to retrieve the

underlying key material of a key object in a suitable format. A KeyFactory

object can be created using the static KeyFactory.getInstance() method.

From a key specification, you can generate the keys using the

generatePublic() and generatePrivate() methods. To get the key

specification from a KeyFactory, you can use the getKeySpec() method.

• The KeyPair class is used to hold a public key and a private key. It

provides getPrivate() and getPublic() methods to get the private and the

public keys respectively.



306



Java 2 Network Security



• The KeyPairGenerator class is used to generate key pairs, while the

KeyPairGeneratorSpi class is used to define the SPI for the

KeyPairGenerator class.

A KeyPairGenerator object can be created using the getInstance() static

method for the KeyPairGenerator class. A KeyPairGenerator object must

be initialized before it can generate keys. To do this, four methods are

provided, all of them called initialize(), but each having a different

signature. The four initialize() methods allow you to:

• Generate a key pair in an algorithm-independent manner or in an

algorithm-specific manner.

• Provide a specific source of randomness or use the SecureRandom

implementation of the highest-priority installed provider as the source

of randomness.

When you initialize a key pair in an algorithm-independent manner, you

specify the key size. If you initialize in an algorithm-specific way, you

supply the AlgorithmParameterSpec to the generator.

In the algorithm-independent case, it is up to the provider to determine the

algorithm-specific parameters to be associated with each of the keys. The

provider may use pre-computed parameter values, or may generate new

values. For example:



KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA");

SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");

random.setSeed(userSeed);

keyGen.initialize(1024, random);



In the algorithm-specific case, the user supplies the parameters to

initialize a key pair:



KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA");

DSAParameterSpec dsaSpec = new DSAParameterSpec(p, q, g);

SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");

random.setSeed(userSeed);

keyGen.initialize(dsaSpec, random);



• The KeyStore class is used to represent an in-memory collection of keys

and certificates, while the KeyStoreSpi class defines the SPI for the

KeyStore class. You can generate a KeyStore object using the static

getInstance() method. To load a KeyStore object from an InputStream, the

load() method is provided. The store() method can be used to store the

keystore to an OutputStream.



Security APIs in Java 2



307



The KeyStore class provides methods to get and set keys and certificates

from the keystore. For instance, aliases() lists all the alias names in the

keystore, deleteEntry() deletes the entry identified by a specific alias from

the keystore, and getKey() gets the key associated with a given alias from

the keystore.

10.1.6.1 An Example of Keystore Management

The following program loads a KeyStore object, gets a key with alias marco

from it and stores it into another KeyStore object. In other words, this code

performs exactly the same function as the -export and -import commands

associated with the keytool utility (see 9.1, “Key and Certificate Management

Tool” on page 259).

The code comments explain the operations in detail:



class KeyStoreManagement

{

public static void main(String[] args)

{

try

{

// create the Keystore object

KeyStore ks = KeyStore.getInstance("JKS", "SUN");

String keypass = "marcop";

char[] pwd = new char[6];

for (int i = 0; i < pwd.length; i++)

pwd[i] = keypass.charAt(i);

// load the keystore from the system

FileInputStream fisk = new

FileInputStream("D:\\itso\\ch10\\keystore1\\marcostore");

ks.load(fisk, pwd);

// get the certificate from the keystore with alias marco

// similar to keytool -export

X509Certificate certs = (X509Certificate)ks.getCertificate("marco");

// Storing the same certificate in other keystore.

// create the keystore object

KeyStore itsostore = KeyStore.getInstance("JKS", "SUN");

FileInputStream fisk1 = new

FileInputStream("D:\\itso\\ch10\\keystore2\\marcostore");

Figure 130. (Part 1 of 2). KeyStoreManagement.java



308



Java 2 Network Security



Xem Thêm
Tải bản đầy đủ (.pdf) (702 trang)

×