引言
安全性是 Globus Toolkit_ Grid 安全性基础结构(Globus Toolkit_ Grid security infrastructure,GSI)的核心,GSI 支持通过计算机网络进行的安全验证和通信。它提供了诸如相互验证和单一登录这样的服务,并且基于公认的标准,比如公钥加密、X.509 认证以及安全套接字层(Secure Sockets Layer,SSL)。
本文旨在解决在使用 IBM 的 WebSphere Application Server 编写支持 Web 的网格应用程序时遇到的兼容性问题。这些问题与 GSI 用来实现各种安全性服务的 Grid 安全性基础结构 Java crypto 扩展密切相关。
Globus Toolkit 安全性库
Globus Toolkit 3.0(GT3)使用一个由 Java CoG Kit 1.1 提供的全新安全性库。这个新的安全性库基于 GSS-API,并且是完全使用开放源码 SSL 和认证处理库实现的。当为需要 X.509 证书和代理(也称为 Globus 凭证)处理的 WebSphere 平台编写支持 Web 的网格应用程序时,冲突就会出现。之所以出现这些冲突,是因为 WebSphere 使用 IBM JCE 作为缺省的安全性提供者,而 GT3 将试图使用它自己的安全性扩展(由 Bouncy Castle 的 Legion 提供)和 SSLv3/TLS 实现(由 Claymore Systems 许可)。
1. 使用 WebSphere 从缺省证书创建代理
我们将首先编写一个简单的 JSP 页面来创建 Globus 凭证(也称为代理),如图 1 所示。
清单 1. 一个用于创建 Globus 凭证的 JSP 文件
<%@ page
language="java"
contentType="text/html; charset=ISO-8859-1"
import= "java.util.Properties,
java.io.*,
java.security.cert.*,
org.globus.gsi.*,
org.globus.gsi.bc.*,
org.apache.log4j.*"
%>
<%!
public synchronized GlobusCredential gridProxyInit(InputStream inUserCert, InputStream inUserKey ,
String pwd, int bits, int hours)
throws IOException, java.security.GeneralSecurityException
{
X509Certificate userCert = CertUtil.loadCertificate(inUserCert);
OpenSSLKey key = new BouncyCastleOpenSSLKey(inUserKey);
System.out.println("gridProxyInit: User Cert=" + userCert + " User key encrypted=" + key.isEncrypted());
if (key.isEncrypted()) {
try {
key.decrypt(pwd);
} catch(java.security.GeneralSecurityException e) {
throw new java.security.GeneralSecurityException("Wrong password or other security error");
}
}
System.out.println("gridProxyInit: User Priv key : " + key.getPrivateKey());
java.security.PrivateKey userKey = key.getPrivateKey();
BouncyCastleCertProcessingFactory factory =
BouncyCastleCertProcessingFactory.getDefault();
return factory.createCredential(new X509Certificate[] {userCert},
userKey,
bits,
hours * 3600, GSIConstants.GSI_2_PROXY, null);
}
%>
<%
Logger.getRootLogger().setLevel(Level.DEBUG);
String action = (request.getParameter("ACTION") != null) ? request.getParameter("ACTION") : "INIT";
String pwd = request.getParameter("txtPWD");
String bits = request.getParameter("ST");
String hours = request.getParameter("LIFETIME");
String Msg = request.getParameter("MSG");
String subject = "N/A"; // generated proxy subject
String issuer = "N/A";
String strength = "N/A"; // proxy strength
String sTimeLeft = "N/A"; // proxy time left
// Used to load certs from db
boolean proxyReady = false; // is the proxy ready?
// Init
if ( ! action.equalsIgnoreCase("INIT") ) {
}
try {
if ( action.equalsIgnoreCase("CREATE") ) {
String path = System.getProperty("user.home") + "/.globus/cog.properties";
org.globus.common.CoGProperties props = new org.globus.common.CoGProperties();
props.load(path);
String certPath = props.getProperty("usercert");
String keyPath = props.getProperty("userkey");
System.out.println("test-proxy.jsp::CREATE pwd=" + pwd + " bits=" + bits + " hours=" + hours);
System.out.println("test-proxy.jsp::Cert path:" + certPath + "\nKey path: " + keyPath);
// create a globus cred. Certs read from def locs
InputStream inCert = new FileInputStream(certPath);
InputStream inKey = new FileInputStream(keyPath);
int stren = Integer.parseInt(bits);
int life = Integer.parseInt(hours);
// create globus proxy
GlobusCredential cred = gridProxyInit(inCert , inKey, pwd , stren, life);
subject = cred.getSubject();
issuer = cred.getIssuer();
strength = new Integer(cred.getStrength()).toString();
sTimeLeft = org.globus.util.Util.formatTimeSec(cred.getTimeLeft()); //
// save proxy in def location
ByteArrayOutputStream bos = new ByteArrayOutputStream();
cred.save(bos);
System.out.println("grid-proxy.jsp::Saving proxy into db\n" + bos.toString());
proxyReady = true;
Msg ="Proxy created.";
}
}
catch ( Exception e ) {
e.printStackTrace();
response.sendRedirect("test-proxy.jsp?MSG=GridProxyInit+Error:" + e.getMessage());
}
%>
test-proxy.jsp
Grid Proxy Init
Cetificates will be loaded from default locations
<% if ( Msg != null ) { %>
<%=Msg%>
<% } %>
为了在 WebSphere Studio V5.x 中成功地编译这个 JSP 文件,必须确保下面这些 JAR 文件在您的 WEB-INF/lib 文件夹中:
cog-jglobus.jar
cryptix-ans1.jar
cryptix32.jar
jce-jdk13-117.jar
jgss.jar
log4j-core.jar
puretls.jar.
上面所有的文件都包括在 GT3 发行版中。
2. 配置用于测试的本地证书
您必须配置这个 JSP 文件抑或 Java CoG Kit 将要使用的本地证书。文件 cog.properties 必须在您的本机 .globus 文件夹中。因而,如果您以“Administrator”的身份登录到您的 win32 系统,您就必须创建:C:\Documents and Settings\Administrator\.globus\cog.properties。
此文件的样本内容如清单 2 所示:
清单 2. cog.properties 文件
#Tue Aug 26 11:46:58 EDT 2003
usercert=C\:\\Documents and Settings\\Administrator\\.globus\\usercert.pem
userkey=C\:\\Documents and Settings\\Administrator\\.globus\\userkey.pem
proxy=C\:\\DOCUME~1\\ADMINI~1\\LOCALS~1\\Temp\\x509up_u_vladimir
cacert=C\:\\Documents and Settings\\Administrator\\.globus\\simpleCA\\cacert.pem
如果您从 Globus Web 站点 下载并安装 Java CoG Kit 的话,cog.properties 文件将会自动设置。当这一切都准备就绪之后,在您的 Web 浏览器上 JSP 文件看上去会类似于图 1。
图 1. 代理的初始测试
该 JSP 的输出如下:
图 2. 创建 Globus 凭证
到目前为止一切顺利:我们已经成功地从我们的 Web 应用程序创建了一个 Gloubs 代理。
3. 创建证书和私钥
清单 3 中的 JSP 代码创建了用户证书和私钥集。
清单 3. 创建证书和私钥对
<%@ page
language="java"
contentType="text/html; charset=ISO-8859-1"
import= "java.util.*,
java.io.*,
java.security.*,
java.security.cert.*,
org.globus.gsi.*,
org.globus.gsi.bc.*,
org.apache.log4j.*,
COM.claymoresystems.cert.*,
cryptix.util.mime.*"
%>
<%!
/*
* Write bytes into a PEM string
*/
public static String writePEM (byte[] bytes, String hdr, String ftr)
throws IOException
{
ByteArrayOutputStream bos=new ByteArrayOutputStream();
Base64OutputStream b64os=new Base64OutputStream(bos);
b64os.write(bytes);
b64os.flush();
b64os.close();
ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
InputStreamReader irr=new InputStreamReader(bis);
BufferedReader r=new BufferedReader(irr);
StringBuffer buff = new StringBuffer();
String line;
buff.append(hdr);
while((line=r.readLine())!=null){
buff.append(line + "\n");
}
buff.append(ftr);
return buff.toString();
}
/* Create an X509 Name used for cert creation */
private static X509Name makeCertDN(String subject) throws Exception
{
Vector tdn = new Vector();
Vector elems = new Vector();
StringTokenizer st = new StringTokenizer(subject,",");
for (; st.hasMoreTokens() ;) {
String s = st.nextToken(); // [key=value]
if ( s.indexOf("=") == -1 )
throw new Exception("Invalid subject format: " + subject + " Offending value: " + s);
String key = s.substring(0, s.indexOf("=")).trim();
String val = s.substring(s.indexOf("=") + 1).trim();
if ( val == null || val.equals(""))
throw new Exception("Invalid subject format: " + subject + " Offending value: " + s);
String[] temp = {key, val};
tdn.addElement(temp);
}
// COM.claymoresystems.cert (puretls.jar)
return CertRequest.makeSimpleDN(tdn);
}
/* Cert/key creation */
public void generateSelfSignedCertAndKey(String subject, int bits,
String Pwd, StringWriter swKey, StringWriter swCert )
throws NoSuchAlgorithmException, Exception
{
X509Name _subject = makeCertDN(subject);
System.out.println("generateSelfSignedCertAndKey Cert subject: " + _subject.getNameString() +
" Strength=" + bits + " Pwd=" + Pwd);
// Generate A Cert RQ
//StringWriter sw = new StringWriter(); // wil contain the priv key PEM
BufferedWriter bw = new BufferedWriter(swKey);
KeyPair kp = CertRequest.generateKey("RSA", bits, Pwd, bw, true);
// certs are valid for 1 year: 31536000 secs
byte[] certBytes = CertRequest.makeSelfSignedCert(kp, _subject, 31536000);
BufferedWriter bw1 = new BufferedWriter(swCert);
String _certPEM = writePEM(certBytes,
"-----BEGIN CERTIFICATE-----\n",
"-----END CERTIFICATE-----\n");
bw1.write(_certPEM);
System.out.println("CertKeyGenerator: Signed Cert RQ . signedUserCert\n" + _certPEM);
}
%>
<%
org.apache.log4j.Logger.getRootLogger().setLevel(org.apache.log4j.Level.DEBUG);
String action = (request.getParameter("ACTION") != null) ? request.getParameter("ACTION") : "INIT";
String Msg = request.getParameter("MSG");
try {
//Properties args = com.ibm.grid.ogsa.securityextension.gsi.GSIProperties.load();
String msg = "";
if ( action.equalsIgnoreCase("GENERATE") ) {
String cn = request.getParameter("txtCN");
String org = request.getParameter("txtORG");
String ou = request.getParameter("txtOU");
String pwd = request.getParameter("txtPWD");
String subject = "O=" + org + "," + "OU="+ ou + ",CN=" + cn;
System.out.println("Gen certs. Subject: " + subject);
StringWriter swCert = new StringWriter(); // wil contain cert PEM
StringWriter swKey = new StringWriter(); // wil contain the priv key PEM
generateSelfSignedCertAndKey(subject, 1024, pwd, swKey, swCert);
// Private key
System.out.println("Private key PEM\n" + swKey.toString());
// cert
System.out.println("Certificate PEM\n" + swCert.toString());
response.sendRedirect("test-creds.jsp?MSG=Certs+installed+successfully.");
}
}
catch ( Exception e0 ) {
e0.printStackTrace();
response.sendRedirect("test-creds.jsp?MSG=Error+ installing+certs:" + e0.getMessage());
}
catch ( InternalError e ) {
e.printStackTrace();
response.sendRedirect("test-creds.jsp?MSG=Error+ installing+certs:" + e.getMessage());
}
%>
setup-gsi.jsp
Setup Certificate/Private key
<% if ( Msg != null ) { %>
<%=Msg%>
<% } %>
All fileds are required
这段代码在您的 Web 浏览器上会这样显示:
图 3. 设置证书/私钥
当尝试创建认证/私钥时,WebSphere 将会抛出下面的异常:
清单 4. 由 WebSphere 和 GT3 之间发生的安全提供者冲突所抛出的异常
java.lang.InternalError: java.security.NoSuchAlgorithmException: class configured for Cipher:
com.ibm.crypto.provider.DESedeCipher is not a subclass of xjava.security.Cipher
at COM.claymoresystems.crypto.PEMData.writePEMObject(PEMData.java:172)
at COM.claymoresystems.crypto.EAYEncryptedPrivateKey.writePrivateKey(EAYEncryptedPrivateKey.java:83)
at COM.claymoresystems.cert.CertRequest.generateKey(CertRequest.java:102)
at jsp.article._test_2D_creds_jsp_0.generateSelfSignedCertAndKey(_test_2D_creds_jsp_0.java:108)
at jsp.article._test_2D_creds_jsp_0._jspService(_test_2D_creds_jsp_0.java:210)
通过更仔细地分析这个消息:
class configured for Cipher: com.ibm.crypto.provider.DESedeCipher is not a subclass of xjava.security.Cipher;
并且进行栈跟踪,我们可以假定 Java CoG API 内部尝试使用 IBM 的 JCE 作为缺省的提供者,这是不正确的,因为安全性提供者应该为 Cryptix/Bouncy Castle。这个问题在创建私钥时会出现。为了解决这个问题,将需要修改 Java 代码。
4. 更改 GT3 安全性提供者以与 WebSphere 协同工作
在确定了问题之后,就可以通过更改 puretls.jar 和 cryptix32.jar 源码中的几行代码来进行解决。为此,您将需要从 Web 站点下载源码。下表列出了需要进行的实际代码更改。要获得关于下载源码的信息,请参阅参考资料部分。
表 1:对 GT3 安全提供者进行的更改一览
JAR 文件 类/行 原文 更新 注解
Puretls.jar PEMData.java
Line 155 Cipher ciph = Cipher.getInstance(algorithm); ((FeedbackCipher)ciph)
.setInitializationVector(iv); Cipher ciph = Cipher.getInstance(algorithm,"Cryptix"); 这种更改将强制要求使用 Cryptix 作为安全性提供者。
Cryptix32.jar Cipher.java
Line 480 cipherName = IJCE.getStandardName (cipherName, "Cipher"); cipherName = CryptixProperties.getProperty ("Alg.Alias.Cipher." + cipherName); 方法 IJCE. getStandardName 已经受到反对,并且在 WebSphere 中错误地返回 Cipher 名称(对于像 DES-EDE3 这样的密码)。
结束语
本文给 GT3 安全性提供者以及它们与 WebSphere Application Server V4 和 V5 的兼容性提供了有益的见解。如果您正在编写支持 Web 的网格应用程序,并且需要处理或创建证书、密钥和代理,那么这里的信息会帮助解决您在 GT3 和 WebSphere 容器之间可能会遇到的不兼容性问题。
|