Oracle8i Enterprise JavaBeans and CORBA Developer's Guide Release 2 (8.1.6) A81356-01 |
|
In the simple cases, a client starts a new server session implicitly when it activates a server object, such as an EJB or a CORBA server object. But Oracle8i also gives you the ability to control session start-up explicitly, either from the client or from a server object.
In general, when you look up a published object using the URL notation and you specify a hostname and port, then the object is activated in a new session. For example when an activated CORBA server object or an EJB looks up a second server object, using the same series of statements as the first client would use:
Hashtable env = new Hashtable(); env.put(Context.URL_PKG_PREFIXES, "oracle.aurora.jndi"); env.put(Context.SECURITY_PRINCIPAL, "scott"); env.put(Context.SECURITY_CREDENTIALS, "tiger"); env.put(Context.SECURITY_AUTHENTICATION, ServiceCtx.NON_SSL_LOGIN); Context ic = new InitialContext(env); SomeObject myObj = (SomeObject) ic.lookup("sess_iiop://localhost:5521:ORCL/test/someobject");
then the object myObj
is activated in a separate session from the session in which the server object that did the lookup is running.
If the server object must look up and activate a new published object in the same session in which it is running, then the server object should use the thisServer/:thisSession notation in place of the hostname:port:SID
in the URL. For example, to look up and activate an object in the same session, do the following:
Hashtable env = new Hashtable(); env.put(Context.URL_PKG_PREFIXES, "oracle.aurora.jndi"); Context ic = new InitialContext(env); SomeObject myObj = (SomeObject) ic.lookup("sess_iiop://thisServer/:thisSession/test/someobject");
In this case, myObj
is activated in the same session in which the invoking object is running. Note that there is no need to supply login authentication information, as the client (a server object in this case) is already authenticated to Oracle8i.
Realize that objects are not authenticated, instead, clients must be authenticated to a session. However, when a separate session is to be started, then some form of authentication must be done--either login or SSL credential authentication.
In the simple case, you let the JNDI initial context lookup()
method also start the session and authenticate the client. The session then becomes the default session (and has the name :default
).
If you then create additional objects in the client and activate them, the new objects run in the same session. Even if you create a new JNDI initial context and look up the same or a new object using that context, the object is instantiated in the same session as the first object.
There are cases, however, when a client needs to activate an object in a separate session from any current objects. Do this as follows:
ServiceCtx service = (ServiceCtx) ic.lookup( "sess_iiop://localhost:2481:ORCL");
createSubcontext()
on the service context.
SessionCtx new_session = (SessionCtx) service.createSubcontext( ":session1");
Name the new session in the parameter to createSubcontext()
, for example ":session1". The name must start with a colon (':') and cannot contain a slash ('/').
login()
method on the new session:
new_session.login("scott", "tiger", null);
The following is a more detailed code example that demonstrates this technique. The complete example is in "twosessions".
Hashtable env = new Hashtable (); env.put (Context.URL_PKG_PREFIXES, "oracle.aurora.jndi"); Context ic = new InitialContext (env); // Get a SessionCtx that represents a database instance ServiceCtx service = (ServiceCtx) ic.lookup ("sess_iiop://localhost:2481:ORCL"); // Create and authenticate a first session in the instance. SessionCtx session1 = (SessionCtx) service.createSubcontext (":session1"); // Authenticate session1.login("scott", "tiger", null); // Create and authenticate a second session in the instance. SessionCtx session2 = (SessionCtx) service.createSubcontext (":session2"); // Authenticate using a login object (not required, just shown for example). LoginServer login_server2 = (LoginServer)session2.activate ("etc/login"); Login login2 = new Login (login_server2); login2.authenticate ("scott", "tiger", null); // Activate one Hello object in each session Hello hello1 = (Hello)session1.activate (objectName); Hello hello2 = (Hello)session2.activate (objectName); // Verify that the objects are indeed different hello1.setMessage ("Hello from Session1"); hello2.setMessage ("Hello from Session2"); System.out.println (hello1.helloWorld ()); System.out.println (hello2.helloWorld ());
You can release objects that you have instantiated in a session by sending a _release()
message to them. For example, if you previously activated an object named bank
, then invoking bank._release()
deactivates the bank
object.
When the last object activated in a session is released, the connection is closed.
You can also invoke orb.shutdown()
from a client program. This method releases all objects and connections from the client.
This section describes in greater detail how you can explicitly activate a session IIOP service and then activate one or more Oracle8i sessions in the context of the service. The simplest way to activate services and sessions is to use the JNDI methods provided in the ServiceCtx
and SessionCtx
classes.
This section demonstrates service and session activation, as well as explicit login authentication, by way of a useful example: lister.java
. This program recursively lists the names of all published objects in the session name space, along with the creation dates and owners.
Unlike most of the other example programs in this guide, the lister program does not start by activating a published object. In the other example programs, the service and session are usually started automatically, as a by-product of the published object look up. In this example the service and session must be specifically activated by the client program.
The example starts by instantiating a new hashtable for the environment properties to be passed to the server:
Hashtable env = new Hashtable(); env.put(Context.URL_PKG_PREFIXES, "oracle.aurora.jndi");Note that only the
URL_PKG_PREFIXES
Context
variable is filled in--the other information will be provided in the login.authenticate()
method parameters.
Next, create a new JNDI Context. This is the necessary first step in all programs that will use JNDI methods. Pass in the hashtable, as usual.
Context ic = new InitialContext(env);Then use the JNDI
lookup()
method on the initial context, passing in the service URL, to establish a service context. This example uses a service URL with the service prefix, hostname, listener port, and SID:
ServiceCtx service = (ServiceCtx) ic.lookup("sess_iiop://localhost:2481:ORCL");
The next step is to initiate a session. Do this by invoking the createSubcontext()
method on the service context object, as follows:
SessionCtx session = (SessionCtx) service.createSubcontext(":session1");
Note that you must name a new session when you create it. The session name must start with a colon (:), and cannot contain a slash ('/'), but is not otherwise restricted.
The final step before you can access the published object tables is to authenticate the client program to the database. Do this by calling the login()
method on the session context object:
session.login("scott", "tiger", null); // role is null
Finally, the example starts listing by calling the listOneDirectory()
static method, which recursively lists all directories (PublishingContext
s) and leafs (PublishedObject
s) in the published names hierarchy:
listOneDirectory ("/", session);
The complete code for the example is reproduced in the following section. The code includes some minimal formatting to align the printed output. Follow the same procedures as for the sample applications in Appendix A, "Example Code: CORBA" to compile and run this example.
import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingEnumeration; import javax.naming.Binding; import javax.naming.NamingException; import javax.naming.CommunicationException; import oracle.aurora.jndi.sess_iiop.ServiceCtx; import oracle.aurora.jndi.sess_iiop.SessionCtx; import oracle.aurora.jndi.sess_iiop.ActivationException; import oracle.aurora.AuroraServices.PublishedObject; import oracle.aurora.AuroraServices.objAttribsHolder; import oracle.aurora.AuroraServices.objAttribs; import oracle.aurora.AuroraServices.ctxAttribs; import oracle.aurora.jts.client.AuroraTransactionService; import oracle.aurora.AuroraServices.LoginServer; import oracle.aurora.client.Login; import javax.naming.Context; import javax.naming.InitialContext; import java.util.Hashtable; public class Lister { public static void main (String[] args) throws Exception { if (args.length != 3) { System.out.println("usage: Lister serviceURL user password"); System.exit(1); } String serviceURL = args [0]; String username = args [1]; String password = args [2]; // Prepare a simplified Initial Context as we are going to do // everything by hand. Hashtable env = new Hashtable(); env.put(Context.URL_PKG_PREFIXES, "oracle.aurora.jndi"); Context ic = new InitialContext(env); // Get a SessionCtx that represents a database instance. ServiceCtx service = (ServiceCtx) ic.lookup(serviceURL); // Create a session in the instance. // The session name must start with a colon(:). SessionCtx session = (SessionCtx) service.createSubcontext(":session1"); session.login(username, password, null); // Print a header line. System.out.println ("\n\nName Create Date Owner"); listOneDirectory ("/", session); } public static void listOneDirectory (String name, SessionCtx ctx) throws Exception { System.out.print(name); for (int i = name.length(); i < 30; i++) System.out.print(" "); ctxAttribs attribs = null; try { attribs = ctx.getAttributes(); } catch (org.omg.CORBA.NO_PERMISSION e) { return; } System.out.print(attribs.creation_ts); for (int i = 30 + attribs.creation_ts.length(); i < 55; i++) System.out.print(" "); System.out.print(attribs.owner); /* * You could also add output for the access permissions: * attribs.read * attribs.write * attribs.execute */ System.out.println(); // Show the sub entries listEntries(ctx, name); } public static void listEntries (Context context, String prefix) throws Exception { NamingEnumeration bindings = context.list(""); while (bindings.hasMore()){ Binding binding = (Binding) bindings.next(); String name = binding.getName(); Object object = context.lookup(name); if (object instanceof SessionCtx) listOneDirectory(prefix + name + "/", (SessionCtx) object); else if (object instanceof PublishedObject) listOneObject(prefix + name, (PublishedObject) object); else // We should never get here. System.out.println(prefix + name + ": " + object.getClass()); } } public static void listOneObject (String name, PublishedObject obj) throws Exception { objAttribsHolder holder = new objAttribsHolder(); try { obj.get_attributes(holder); } catch (org.omg.CORBA.NO_PERMISSION e) { return; } objAttribs attribs = holder.value; System.out.print(name); for (int i = name.length(); i < 30; i++) System.out.print(" "); System.out.print(attribs.creation_ts); for (int i = 30 + attribs.creation_ts.length(); i < 55; i++) System.out.print(" "); System.out.print(attribs.owner); /* * You could also add output for: * attribs.class_name * attribs.schema * attribs.helper * and the access permissions: * attribs.read * attribs.write * attribs.execute */ System.out.println(); } }
Starting a new session from a CORBA server object or from an EJB is identical to starting a session from an application client. You can start the session implicitly by using lookup
() on an initial context to look up and activate another published object, or you can start a new service context, and from that a new session, just as "Starting a Named Session From a Client" shows.
Hashtable env = new Hashtable (); env.put (Context.URL_PKG_PREFIXES, "oracle.aurora.jndi"); Context ic = new InitialContext (env); employee = (Employee)ic.lookup ("sess_iiop://thisServer/test/myEmployee");
Any new session connection must always authenticate itself to the database server. See "clientserverserver" for an example that starts a new session from a CORBA server object.
If you need to activate a new object in the same session from another server object, use the thisServer
indicator. See "Using thisServer" for more information.
A session normally ends when the last client connection terminates. However, a server object can control the session duration by using the oracle.aurora.net.Presentation.sessionTimeout()
method. The method takes one parameter; the session timeout value in seconds. The session timeout clock starts ticking when the last client request completes. For example:
int timeoutValue = 30; ... // set the timeout to 30 seconds oracle.aurora.net.Presentation.sessionTimeout(timeoutValue); ... // set the timeout to a very long time oracle.aurora.net.Presentation.sessionTimout(Integer.MAX_INT);
See the example "timeout" for an example that sets session timeout on the server side.
To terminate a database session, use the exitSession()
method. For example,
oracle.aurora.vm.OracleRuntime.exitSession(1);
The int parameter for exitSession(int x)
is an exit value, similar to the value supplied for System.exit()
.
As discussed in the Oracle8i Java Developer's Guide, there are several security issues you must think about for your application. The Oracle8i Java Developer's Guide divides security into network connection, database contents, and JVM security issues. All these issues are pertain to IIOP. However, IIOP has specific implementation issues for both the networking and the JVM security, as listed below:
loadjava
tool. See the loadjava
discussion in the Oracle8i Java Developer's Guide for information on granting execution rights when loading the CORBA classes.
- Session authorization--The session is authorized to the user. In this case, the client is authorized to access the server through validating either the username or certificate provided.
- User authorization--The client or server can perform authorization on a provided certificate. This type of authorization can be performed only when the client or server authenticates itself by providing a certificate.
This section describes fully the network connection security issues that IIOP applications must consider.
Do you want your transport line to be encrypted? Do you want data integrity and confidentiality? If you believe that the physical connection can be tampered with, you can consider encrypting all transmissions by using the secure socket layer (SSL) encryption technology. However, because adding encryption to your transmission affects your connection performance, if you do not have any transport security issues, you should transmit unencrypted.
JServer's CORBA and EJB implementations rely on the Secure Socket Layer (SSL) for data integrity and authentication. SSL is a secure networking protocol, originally defined by Netscape Communications, Inc. Oracle8i JServer supports SSL over the IIOP protocol used for the ORB.
When a connection is requested between a client and the server, the SSL layer within both parties negotiate during the connection handshake to verify if the connection is allowed. The connection is verified at several levels:
The SSL layer performs authentication between the peers. After the handshake, you can be assured that the peers are authenticated to be who they say they are. You can perform additional tests on their certificate chain to authorize that this user can access your application. See "Authorization" on how to go beyond authentication.
SSL makes sure that both the client and server side agree on an SSL protocol version number. The values that you can specify are as follows:
SSL_UNDETERMINED
. This is the default setting.
SSL_30
.
On the database, the default is "Undetermined". The database does not support 2.0 or 3.0 with 2.0 Hello. Thus, you can use only the Undetermined or 3.0 setting for the client.
environment.put("CLIENT_SSL_VERSION", ServiceCtx.SSL_30);
Authentication is the process where one party supplies to a requesting party information that identifies itself. This information guarantees that the originator is not an imposter. In the client/server distributed environment, authentication can be required from the client or the server:
The Oracle data server is a secure server; a client application cannot access data stored in the database without first being authenticated by the database server. Oracle8i CORBA server objects and Enterprise JavaBeans execute in the database server. For a client to activate such an object and invoke methods on it, the client must authenticate itself to the server. The client authenticates itself when a CORBA or EJB object starts a new session. The following are examples of how each IIOP client must authenticate itself to the database:
The client authenticates itself by providing one of the following types:
The type of client-side authentication can be determined by the server's configuration. If, within the SQLNET.ORA file, the SSL_CLIENT_AUTHENTICATION
parameter is TRUE
, the client must provide certificate-based authentication. If SSL_CLIENT_AUTHENTICATION
is FALSE
, the client authenticates itself with a username/password combination. If SSL_CLIENT_AUTHENTICATION
is TRUE
and the client provides a username/password, the connection handshake will fail.
The following table gives a brief overview of the options that the client has for authentication.
As the table demonstrates, most of the authentication options include setting an appropriate value in JNDI properties.
To set up client-side authentication using JNDI, you set the javax.naming.Context.SECURITY_AUTHENTICATION
attribute to one of the following values:
ServiceCtx.NON_SSL_LOGIN
--A plain IIOP connection is used. Because SSL is not used, all data flowing over the line is not encrypted. Thus, to protect the password, the client uses the login protocol to authenticate itself. In addition, the server does not provide SSL certificates to the client to identify itself.
ServiceCtx.SSL_LOGIN
--An SSL-enabled IIOP connection is used. All data flowing over the transport is encrypted. If you do not want to provide a certificate for the client authentication, use the login protocol to provide the username and password.
Because this is an SSL connection, the server sends its certificate identity to the client. The client is responsible for verifying the server's certificate, if interested, for server authentication. Optionally, the client can set up trust points for the server's certificate to be verified against.
ServiceCtx.SSL_CREDENTIAL
--An SSL-enabled IIOP connection is used. All data flowing over the transport is encrypted. The client provides the username and password without using the login protocol for client authentication to the server. The username and password are automatically passed to the server in a security context, on the first message.
The server provides its certificate identity to the client. The client is responsible for verifying the server's certificate, if interested, for server authentication.
ServiceCtx.SSL_CLIENT_AUTH
--An SSL-enabled IIOP connection is used. All data flowing over the transport is encrypted. The client provides appropriate certificates for client-side authentication to the server. In addition, the server provides its certificate identity to the client. If interested, the client is responsible for authorizing the server's certificate.
authenticate()
method.
Within each of these options, you choose to do one or more of the following:
Client authentication |
|
Server authentication |
For information on how to implement each of these methods for client or server authentication, see the following sections:
The client authenticates itself to the database server either through a username/password or by supplying appropriate certificates. The username/password can be supplied either through Oracle's login protocol or credentials over the SSL transport connection.
SECURITY_AUTHENTICATION
to ServiceCtx.SSL_LOGIN
or ServiceCtx.NON_SSL_LOGIN
.
SECURITY_AUTHENTICATION
to serviceCtx.SSL_CREDENTIAL
.
A client can use the login protocol to authenticate itself to the Oracle8i data server. You can use the login protocol either with or without SSL encryption, because a secure handshaking encryption protocol is built in to the login protocol.
If your application requires an SSL connection for client-server data security, specify the SSL_LOGIN service context value for the SECURITY_AUTHENTICATION property that is passed when the JNDI initial context is obtained. The following example defines the connection to be SSL-enabled for the login protocol. Notice that the username and password are set.
Hashtable env = new Hashtable();
env.put(javax.naming.Context.URL_PKG_PREFIXES, "oracle.aurora.jndi");
env.put(javax.naming.Context.SECURITY_PRINCIPAL, username);
env.put(javax.naming.Context.SECURITY_CREDENTIALS, password);
env.put(javax.naming.Context.SECURITY_AUTHENTICATION, ServiceCtx.SSL_LOGIN
);
Context ic = new InitialContext(env);
...
If your application does not use an SSL connection, specify NON_SSL_LOGIN within the SECURITY_AUTHENTICATION parameter as shown below:
env.put(javax.naming.Context.SECURITY_AUTHENTICATION, ServiceCtx.NON_SSL_LOGIN
);
When you specify values for all four JNDI Context variables--URL_PKG_PREFIXES, SECURITY_PRINCIPAL, SECURITY_CREDENTIALS, and SECURITY_AUTHENTICATION--the first invocation of the Context.lookup()
method performs a login automatically.
If the client setting up the connection is not using JNDI look up because it already has an IOR, the user that gave them the IOR for the object should have also passed in a Login object that exists in the same session as the active object. You must provide the username and password in the authenticate
method of the Login object, before invoking the methods on the active object.
Using the ServiceCtx.SSL_CREDENTIAL
authentication type means that the username, password, and, potentially, a role are passed to the server on the first request. Because this information is passed over an SSL connection, the password is encrypted by the transfer protocol, and there is no need for the handshaking that the Login protocol uses. This is slightly more efficient and is recommended for SSL connections.
You can explicitly create and populate a Login object for the database login. Typically, you would do this if you wanted to create and use more than a single session from a client. The following example shows a client creating and logging on to two different sessions. To do this, you must perform the following steps:
// Prepare a simplified Initial Context as we are going to do
// everything by hand Hashtable env = new Hashtable (); env.put (Context.URL_PKG_PREFIXES, "oracle.aurora.jndi"); Context ic = new InitialContext (env); // Get a SessionCtx that represents a database instance ServiceCtx service = (ServiceCtx)ic.lookup (serviceURL); // Create and authenticate a first session in the instance. SessionCtx session1 = (SessionCtx)service.createSubcontext (":session1");LoginServer login_server1 = (LoginServer)session1.activate ("etc/login");
Login login1 = new Login (login_server1);
login1.authenticate (user, password, null);
// Create and authenticate a second session in the instance. SessionCtx session2 = (SessionCtx)service.createSubcontext (":session2");LoginServer login_server2 = (LoginServer)session2.activate ("etc/login");
Login login2 = new Login (login_server2);
login2.authenticate (user, password, null);
// Activate one Hello object in each session Hello hello1 = (Hello)session1.activate (objectName); Hello hello2 = (Hello)session2.activate (objectName);
Client authentication through certificates requires the client sending a certificate or certificate chain to the server; the server verifies that the client is truly who the client said it was and that it is trusted.
You set up the client for certificate authentication through one of the following methods:
You can set up a file that contains the user certificate, the issuer certificate, the entire certificate chain, an encrypted private key, and the trustpoints. Once created, you can specify that the client use the file during connection handshake for client authentication.
After you create a valid wallet, bring up the Wallet Manager and perform the following:
This creates a base-64 encoded file that contains all certificates, keys, and trustpoints that you added within your wallet. For information on how to create the wallet, see the Oracle Advanced Security Administrator's Guide.
SECURITY_AUTHENTICATION
property to ServiceCtx.SSL_CLIENT_AUTH
. Provide the appropriate certificates and trustpoints for the server to authenticate against. Specify the filename and decrypting key in the JNDI properties, as follows:
Values | Set in JNDI Property |
---|---|
Name of the certificate file |
|
Key for decrypting the private key |
|
The following code is an example of how to set up the JNDI properties to define the client certificate file:
Hashtable env = new Hashtable(); env.put(javax.naming.Context.URL_PKG_PREFIXES, "oracle.aurora.jndi"); env.put(javax.naming.Context.SECURITY_PRINCIPAL, <filename>); env.put(javax.naming.Context.SECURITY_CREDENTIAL, <decrypting_key>); env.put(javax.naming.Context.SECURITY_AUTHENTICATION,
ServiceCtx.S
SL_CLIENT_AUTH); Context ic = new InitialContext(env); ...
For example, if your decrypting key is welcome12
and the certificate file is credsFile
, the following two lines would specify these values within the JNDI context:
env.put(Context.SECURITY_CREDENTIALS, "welcome12"); env.put(Context.SECURITY_PRINCIPAL, "credsFile");
You can provide each certificate, private key, and trust point programmatically, by setting each item individually within JNDI properties. Once you populate the JNDI properties with the user certificate, issuer (Certificate Authority) certificate, encrypted private key, and trust points, they are used during connection handshake for authentication. To identify client-side authentication, set the SECURITY_AUTHENTICATION
property to serviceCtx.SSL_CLIENT_AUTH
.
You can choose any method for setting up your certificates within the JNDI properties. All authorization information values must be set up before initializing the context.
The following example declares the certificates as a static variable. However, this is just one of many options. Your certificate must be base-64 encoded. For example, in the following code, the testCert_base64
is a base-64 encoded client certificate declared as a static variable. The other variables for CA certificate, private key, and so on, are not shown, but they are defined similarly.
final private static StringtestCert_base64
= "MIICejCCAeOgAwIBAgICAmowDQYJKoZIhvcNAQEEBQAwazELMAkGA1UEBhMCVVMx" + "DzANBgNVBAoTBk9yYWNsZTEoMCYGA1UECxMfRW50ZXJwcmlzZSBBcHBsaWNhdGlv" + "biBTZXJ2aWNlczEhMB8GA1UEAxMYRUFTUUEgQ2VydGlmaWNhdGUgU2VydmVyMB4X" + "DTk5MDgxNzE2MjIxMloXDTAwMDIxMzE2MjIxMlowgYUxCzAJBgNVBAYTAlVTMRsw" + "GQYDVQQKExJPcmFjbGUgQ29ycG9yYXRpb24xPDA6BgNVBAsUMyoqIFNlY3VyaXR5" + "IFRFU1RJTkcgQU5EIEVWQUxVQVRJT04gT05MWSB2ZXJzaW9uMiAqKjEbMBkGA1UE" + "AxQSdGVzdEB1cy5vcmFjbGUuY29tMHwwDQYJKoZIhvcNAQEBBQADawAwaAJhANG1" + "Kk2K7uOOtI/UBYrmTe89LVRrG83Eb0/wY3xWGelkBeEUTwW57a26u2M9LZAfmT91" + "e8Afksqc4qQW23Sjxyo4ObQK3Kth6y1NJgovBgfMu1YGtDHaSn2VEg8p58g+nwID" + "AQABozYwNDARBglghkgBhvhCAQEEBAMCAMAwHwYDVR0jBBgwFoAUDCHwEuJfIFXD" + "a7tuYNO8bOw1EYwwDQYJKoZIhvcNAQEEBQADgYEARC5rWKge5trqgZ18onldinCg" + "Fof6D/qFT9b6Cex5JK3a2dEekg/P/KqDINyifIZL0DV7z/XCK6PQDLwYcVqSSK/m" + "487qjdH+zM5X+1DaJ+ROhqOOX54UpiAhAleRMdLT5KuXV6AtAx6Q2mc8k9bzFzwq" + "eR3uI+i5Tn0dKgxhCZU=\n"; Hashtable env = new Hashtable(); env.put(Context.URL_PKG_PREFIXES, "oracle.aurora.jndi"); env.put(Context.SECURITY_AUTHENTICATION, ServiceCtx.SSL_CLIENT_AUTH); //decrypting key env.put(Context.SECURITY_CREDENTIALS, "welcome12"); // you may also set the certificates individually, as shown bellow. //User certificate env.put(ServiceCtx.SECURITY_USER_CERT,testCert_base64
); //Certificate Authority's certificate env.put(ServiceCtx.SECURITY_CA_CERT, caCert_base64); //Private key env.put(ServiceCtx.SECURITY_ENCRYPTED_PKEY, encryptedPrivateKey_base64); // setup the trust point env.put(ServiceCtx.SECURITY_TRUSTED_CERT, trustedCert); Context ic = new InitialContext(env);
CORBA clients that do not use JNDI can use AuroraCertificateManager
for setting the user and issuer certificates, the encrypted private key, and the trust points.
AuroraCertificateManager
maintains certificates for your application. For the certificates to be passed on the SSL handshake for the connection, you must set the certificates before an SSL connection is made. The client sets its certificates through AuroraCertificateManager
if client-side authentication is required, and the client does not want to use JNDI properties for setting certificates.
The methods offered by this object allow you to:
Invoking the ORB.resolve_initial_references
method with the parameter SSLCertificateManager
will return an object that can be narrowed to a AuroraCertificateManager
. Example 4-1 shows a code example of the following methods.
This method adds the specified certificate as a trusted certificate. The certificate must be in DER encoded format. The client adds trustpoints through this method for server-side authentication.
When your client wants to authenticate a server, the server sends its certificate chain to the client. You might not want to check every certificate in the chain. For example, you have a chain composed of the following certificates: Certificate Authority, enterprise, business unit, a company site, and a user. If you trust the company site, you would check the user's certificate, but you might stop checking the chain when you get to the company site's certificate, because you accept the certificates above the company sites in the hierarchical chain.
Syntax
void addTrustedCertificate(byte[] derCert);
Parameter | Description |
---|---|
derCert |
The DER encoded byte array containing the certificate. |
This method is invoked by servers that wish to require certificates from client applications. This method is not intended for use by client applications.
Syntax
void requestClientCertificate(boolean need);
Parameter | Description |
---|---|
need |
If true, the client must send a certificate for authentication. If false, no certificate is requested from the client. |
This method sets the certificate chain for your client application or server object and can be invoked by clients or by servers. The certificate chain always starts with the Certificate Authority certificate. Each subsequent certificate is for the issuer of the preceding certificate. The last certificate in the chain is the certificate for the user or process.
Syntax
void setCertificateChain(byte[][] derCertChain)
Parameter | Description |
---|---|
derCertChain |
A byte array containing an array of certificates. |
This method sets the private key for your client application or server object. You must specify the key in PKCS5 or PKCS8 format.
Syntax
void setEncryptedPrivateKey(byte[] key, String password);
Parameter | Description |
---|---|
key |
The byte array that contains the encrypted private key. |
password |
A string containing a password for decrypting the private key. |
This method sets the SSL protocol version that can be used for the connection. A 2.0 Client trying to establish an SSL connection with a 3.0 Server will fail and the converse. We recommend using Version_Undetermined, because it lets the peers establish an SSL connection whether they are using the same protocol version or not. SSL_Version_Undetermined
is the default value.
Syntax
void setProtocolVersion(int protocolVersion);
Parameter | Description |
---|---|
protocolVersion |
The protocol version being specified. The value you supply is defined in |
This example does the following:
AuroraCertificateManager
.
setCertificateChain
.
setEncryptedPrivateKey
.
// Get the certificate manager AuroraCertificateManager cm = AuroraCertificateManagerHelper.narrow( orb.resolve_initial_references("AuroraSSLCertificateManager")); BASE64Decoder decoder = new BASE64Decoder(); byte[] userCert = decoder.decodeBuffer(testCert_base64); byte[] caCert = decoder.decodeBuffer(caCert_base64); // Set my certificate chain, ordered from CA to user. byte[][] certificates = { caCert, userCert };cm.setCertificateChain(certificates);
cm.addTrustedCertificate(caCert);
// Set my private key. byte[] encryptedPrivateKey = decoder.decodeBuffer(encryptedPrivateKey_base64);cm.setEncryptedPrivateKey(encryptedPrivateKey, "welcome12");
Server-side authentication takes place when the server provides certificates for authentication to the client. When requested, the server will authenticate itself to the client, also known as server-side authentication, by providing certificates to the client. The SSL layer authenticates both peers during the connection handshake. The client requests server-side authentication by setting any of the SSL_* values in the JNDI property. See "Using JNDI for Authentication" for more information on these JNDI values.
For server-side authentication, you must set up a database wallet with the appropriate certificates, using the Wallet Manager. See the Oracle Advanced Security Administrator's Guide for information on how to create a wallet.
Note: If the client wants to verify the server against trustpoints or authorize the server, it is up to the client to set up its trustpoints and parse the server's certificates for authorization. See "Authorization" for more information. |
The SSL layer authenticates the peers during the connect handshake. After the handshake, you can be assured that the peer is authenticated to be who they said they are. In addition, since the server has specified, within an Oracle wallet, its trustpoints, the SSL adapter on the server will authorize the client. However, the client has the option of how much authorization is done against the server.
The server automatically has trustpoints established through the installed Oracle Wallet. The trustpoints in the wallet are used to verify the client's certificates. However, if the client wants to verify the server's certificates against certain trustpoints, it can set up its these trustpoints, as follows:
AuroraCertificateManager.addTrustedCertificate
method. See Example 4-2 on how to set a single trustpoint through JNDI.
AuroraCertificateManager.addTrustedCertificate
method.
If the client does not set up trust points, it does not hinder the authorization. That is, JServer assumes that the client trusts the server.
The following example shows how the client sets up its trustpoints through JNDI. The JNDI SECURITY_TRUSTED_CERT
property can take only a single certificate.
// setup the trust point
env.put(ServiceCtx.SECURITY_TRUSTED_CERT
, trustedCert);
The client retrieves the certificates to perform any authorization checks. In the past, you could retrieve the single issuer certificate. Now, you receive the entire issuer certificate chain. You must parse the certificate chain for the information that you need. You can parse the chain through the AuroraCurrent
object.
Note: You must configure the database and listener to be SSL-enabled, as described in "Configuring CORBA and EJB in JServer". |
AuroraCurrent
contains three methods for retrieving and managing the certificate chain. For creating and parsing the certificate chain, you can use the X509Cert
class methods. For information on this class, see Sun Microsystems's JDK documentation. Note that the X509Cert
class manipulates the certificate chain differently in JDK 1.1 than in Java 2.
The AuroraCurrent
class methods are as follows:
getPeerDERCertChain
--obtain the peer's certificate chain, which enables you to verify that the peer is authorized to access your application methods.
getNegotiatedProtocolVersion
--obtain the SSL protocol version being used by the connection, to verify the versioning.
getNegotiatedCipherSuite
--obtain the cipher suite used to encrypt messages passed over the connection, to verify that the encryption is strong enough for your purposes.
When the handshake occurs, the protocol version and the type of encryption used is negotiated. The type of encryption can be full or limited encryption, which complies with the United States legal restrictions. After the handshake completes, the AuroraCurrent can retrieve what was resolved in the negotiation.
The following describes the methods contained within AuroraCurrent
. See Example 4-3 for a code example of these methods.
This method obtains the type of encryption negotiated in the handshake with the peer.
Syntax
String getNegotiatedCipherSuite(org.omg.CORBA.Object peer);
Parameter | Description |
---|---|
peer |
The peer from which you obtain the negotiated cipher. |
Returns
A string one of the following values:
Export ciphers:
Domestic ciphers
This method obtains the peer's certificate chain. After retrieving the chain, you can parse through the certificates within the chain, to authorize the peer to your application.
Syntax
byte [] [] getPeerDERCertificateChain(org.omg.CORBA.Object peer);
Parameter | Description |
---|---|
peer |
The peer from which you obtain its certificate chain. |
Returns
A byte array containing an array of certificates.
This method obtains the negotiated SSL protocol version of a peer.
Syntax
String getNegoriatedProtocolVersion(org.omg.CORBA.Object peer);
Parameter | Description |
---|---|
peer |
The peer from which you obtain the negotiated protocol version. |
Returns
A string with one of the following values:
This example shows how to authorize a peer by retrieving the certificate information using the AuroraCurrent
object.
AuroraCurrent
object, invoke the ORB.resolve_initial_references
method with AuroraSSLCurrent
as the argument.
AuroraCurrent
methods: getNegotiatedCipherSuite
, getNegotiatedProtocolVersion
, and getPeerDERCertChain
.
static boolean verifyPeerCert(org.omg.CORBA.Object obj) throws Exception { org.omg.CORBA.ORB orb = oracle.aurora.jndi.orb_dep.Orb.init(); // Get the SSL currentAuroraCurrent
current = AuroraCurrentHelper.narrow (orb.resolve_initial_references("AuroraSSLCurrent
")); // Check the cipher System.out.println("Negotiated Cipher: " + current.getNegotiatedCipherSuite
(obj)); // Check the protocol version System.out.println("Protocol Version: " + current.getNegotiatedProtocolVersion
(obj)); // Check the peer's certificate System.out.println("Peer's certificate chain : "); byte [] [] certChain = current.getPeerDERCertChain
(obj); //Parse through the certificate chain using the X509Certificate methods System.out.println("length : " + certChain.length); System.out.println("Certificates: "); CertificateFactory cf = CertificateFactory.getInstance("X.509"); //For each certificate in the chain for(int i = 0; i < certChain.length; i++) { ByteArrayInputStream bais = new ByteArrayInputStream(certChain[i]); Certificate xcert = cf.generateCertificate(bais); System.out.println(xcert); if(xcert instanceof X509Certificate) { X509Certificate x509Cert = (X509Certificate)xcert; String globalUser = x509Cert.getSubjectDN().getName(); System.out.println("DN out of the cert : " + globalUser); } } return true; }
It is possible for clients to access server objects without using the JNDI classes shown in the other sections of this chapter. These clients can connect to an Oracle server by using straight CosNaming methods. The following example shows how to do this.
import org.omg.CORBA.Object; import org.omg.CosNaming.*; import oracle.aurora.AuroraServices.*; import oracle.aurora.client.Login; public class Client { public static void main(String args[]) throws Exception { // Parse the args if (args.length != 4) { System.out.println("Must supply host/port, username/ password"); System.exit(1); } String host = "sess_iiop://localhost:2481:ORCL"; String port = "2481"; String username = "scott"; String password = "tiger"; // access the Aurora Names Service Bank.Account account = null; Bank.AccountManager manager = null; try { // Get the Name service Object reference (Only ORB specific thing) PublishingContext rootCtx = null; // See the README file with this demo for more about VisiAurora. rootCtx = VisiAurora.getNameService(host, Integer.parseInt(port)); // Get the pre-published login object reference PublishedObject loginObj = null; LoginServer serv = null; NameComponent[] name = new NameComponent[2]; name[0] = new NameComponent("etc", ""); name[1] = new NameComponent("login", ""); // look up this object in the name service Object lo = rootCtx.resolve(name); // Make sure it is a published object loginObj = PublishedObjectHelper.narrow(lo); // create and activate this object (non- standard call) lo = loginObj.activate_no_helper(); serv = LoginServerHelper.narrow(lo); // Create a client login proxy object and authenticate to the DB Login login = new Login(serv); login.authenticate(username, password, null); // Now create and get the bank object reference PublishedObject bankObj = null; name[0] = new NameComponent("test", ""); name[1] = new NameComponent("bank", ""); // look up this object in the name service Object bo = rootCtx.resolve(name); // Make sure it is a published object bankObj = PublishedObjectHelper.narrow(bo); // create and activate this object (non- standard call) bo = bankObj.activate_no_helper(); manager = Bank.AccountManagerHelper.narrow(bo); account = manager.open("Jack.B.Quick"); float balance = account.balance(); System.out.println ("The balance in Jack.B.Quick's account is $" + balance); } catch (org.omg.CORBA.SystemException ex) { System.out.println("Caught System Ex: " + ex); ex.printStackTrace(); } catch(java.lang.Exception ex) { System.out.println("Caught Unknown Ex: " + ex); ex.printStackTrace(); } } }
You can obtain documentation and other collateral information about JNDI from the following web site:
http://java.sun.com/products/jndi/index.html