 | 级别: 初级 Vladimir Silva (vsilva@us.ibm.com), 软件工程师,Internet Technology, IBM
2003 年 12 月 01 日 本文旨在解决在使用 IBM 的 WebSphereApplication Server 编写支持 Web 的网格应用程序时遇到的兼容性问题。这些问题与 GSI 用来实现各种安全性服务的 Grid 安全性基础结构 Java crypto 扩展密切相关。
© Copyright International Business Machines Corporation 2003. All rights reserved.
引言
安全性是 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 文件
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML>
<HEAD>
<%@ 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 GlobusCredentialgridProxyInit(InputStream inUserCert, InputStream inUserKey ,
Stringpwd, int bits, int hours)
throwsIOException, java.security.GeneralSecurityException
{
X509CertificateuserCert = CertUtil.loadCertificate(inUserCert);
OpenSSLKeykey = 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) {
thrownew java.security.GeneralSecurityException("Wrong password or othersecurity error");
}
}
System.out.println("gridProxyInit:User Priv key : " + key.getPrivateKey());
java.security.PrivateKeyuserKey = key.getPrivateKey();
BouncyCastleCertProcessingFactoryfactory =
BouncyCastleCertProcessingFactory.getDefault();
returnfactory.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 fromdb
boolean proxyReady = false; // is the proxy ready?
// Init
if ( ! action.equalsIgnoreCase("INIT")) {
}
try {
if( action.equalsIgnoreCase("CREATE") ) {
Stringpath = System.getProperty("user.home") + "/.globus/cog.properties";
org.globus.common.CoGPropertiesprops = new org.globus.common.CoGProperties();
props.load(path);
StringcertPath = props.getProperty("usercert");
StringkeyPath = props.getProperty("userkey");
System.out.println("test-proxy.jsp::CREATEpwd=" + pwd + " bits=" + bits + " hours=" + hours);
System.out.println("test-proxy.jsp::Certpath:" + certPath + "\nKey path: " + keyPath);
//create a globus cred. Certs read from def locs
InputStreaminCert = new FileInputStream(certPath);
InputStreaminKey = new FileInputStream(keyPath);
intstren = Integer.parseInt(bits);
intlife = Integer.parseInt(hours);
//create globus proxy
GlobusCredentialcred = 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
ByteArrayOutputStreambos = new ByteArrayOutputStream();
cred.save(bos);
System.out.println("grid-proxy.jsp::Savingproxy into db\n" + bos.toString());
&#
160; proxyReady= true;
Msg="Proxy created.";
}
}
catch ( Exception e ) {
e.printStackTrace();
response.sendRedirect("test-proxy.jsp?MSG=GridProxyInit+Error:"+ e.getMessage());
}
%>
<META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<META name="GENERATOR" content="IBM WebSphere Studio">
<META http-equiv="Content-Style-Type" content="text/css">
<LINK href="../../theme/Master.css" rel="stylesheet" type="text/css">
<TITLE>test-proxy.jsp</TITLE>
<STYLE>
TABLE, lengend {
font-family:Verdana, Arial;
font-size:12px;
}
</STYLE>
<SCRIPT LANGUAGE=javascript>
<!--
var proxyReady = <%=proxyReady%>;
function OnLoad() {
var f = document.F1;
if ( document.all &&proxyReady ) {
f.btnCreate.disabled= true;
&
#160; f.txtPWD.disabled= true;
}
}
function showMsg(m
sg){
window.status = msg;
}
// fires when create btn is pressed
function Proxy_OnSubmit() {
var action = "test-proxy.jsp?ACTION=CREATE";
var f = document.F1;
if ( f.txtPWD.value =="" ) {
alert("Allfields are required");
f.txtCN.focus();
returnfalse;
}
//alert("action="+ action + "\nSubmit vals. cert=" + cert + " key="+ key);
showMsg("Working.Please wait...");
if ( document.all )
document.F1.btnCreate.disabled= true; // IE only
document.F1.action = action;
document.F1.submit(); //submit
}
//-->
</SCRIPT>
</HEAD>
<BODY onload="OnLoad()">
<H1>Grid Proxy Init</H1>
<B>Cetificates will be loaded from default locations</B>
<hr>
<% if ( Msg != null ) { %>
<br>
<font color=red><%=Msg%></font>
<% } %>
<FORM method="POST" name="F1" onsubmit="return Proxy_OnSubmit()">
<FIELDSET>
<LEGEND>Proxy Options</LEGEND>
<TABLE width="100%">
<trclass="smallfont">
<TDclass="smallfont">Lifetime</TD>
<TDclass="smallfont">
<INPUTtype="radio" name="LIFETIME" value="12" checked="true">12h
<INPUTtype="radio" name="LIFETIME" value="24">24h
<INPUTtype="radio" name="LIFETIME" value="168">1week
<INPUTtype="radio" name="LIFETIME" value="720">1month
</TD>
</tr>
<trclass="smallfont">
<TDclass="smallfont">Strength</TD>
<TDclass="smallfont">
<INPUTtype="radio" name="ST" value="512" checked="true">512
<INPUTtype="radio" name="ST" value="1024">1024
<INPUTtype="radio" name="ST" value="2048">2048
<INPUTtype="radio" name="ST" value="4096">4096
</TD>
</tr>
</TABLE>
</FIELDSET>
<TABLE width="100%">
<TR>
<tdclass="smallfont">Pass Phrase</td>
<tdclass="smallfont">
<INPUTname="txtPWD" type="password" maxlength="20">
</td>
</TR>
<TR>
<TD> </TD>
<td>
<INPUTname="btnCreate" type="submit" value="Create"align="right">
</td>
</TR>
</TABLE>
<FIELDSET>
<LEGEND>Proxy Info</LEGEND>
<TABLE width="100%">
<TRclass="smallfont">
<tdclass="smallfont">CN</td>
<tdclass="smallfont">
<fontcolor=red></font>
</td>
</TR>
<TR>
<tdclass="smallfont">Subject</td>
<tdclass="smallfont"><%=subject%>
</td>
</TR>
<TR>
<tdclass="smallfont">Issuer</td>
<tdclass="smallfont"><%=issuer%>
</td>
</TR>
<TR>
<tdclass="smallfont">Time left</td>
<tdclass="smallfont"><%=sTimeLeft%>
</td>
</TR>
<TR>
<tdclass="smallfont">Strength</td>
<tdclass="smallfont"><%=strength%>
</td>
</TR>
</TABLE>
</FIELDSET>
<br>
</FORM>
</BODY>
</HTML>
|
为了在 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. 创建证书和私钥对
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML>
<HEAD>
<%@ 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 PEMstring
*/
public static String writePEM(byte[] bytes, String hdr, String ftr)
throwsIOException
{
ByteArrayOutputStreambos=new ByteArrayOutputStream();
Base64OutputStreamb64os=new Base64OutputStream(bos);
b64os.write(bytes);
b64os.flush();
b64os.close();
ByteArrayInputStreambis=new ByteArrayInputStream(bos.toByteArray());
InputStreamReaderirr=new InputStreamReader(bis);
BufferedReaderr=new BufferedReader(irr);
StringBufferbuff = new StringBuffer();
Stringline;
buff.append(hdr);
while((line=r.readLine())!=null){
buff.append(line+ "\n");
}
buff.append(ftr);
returnbuff.toString();
}
/* Create an X509 Nameused for cert creation */
private static X509NamemakeCertDN(String subject) throws Exception
{
Vectortdn = new Vector();
Vectorelems = new Vector();
StringTokenizerst = new StringTokenizer(subject,",");
for(; st.hasMoreTokens() ;) {
Strings = st.nextToken(); // [key=value]
if( s.indexOf("=") == -1 )
thrownew Exception("Invalid subject format: " + subject + " Offendingvalue: " + s);
Stringkey = s.substring(0, s.indexOf("=")).trim();
Stringval = s.substring(s.indexOf("=") + 1).trim();
if( val == null || val.equals(""))
thrownew Exception("Invalid subject format: " + subject + " Offendingvalue: " + s);
String[]temp = {key, val};
tdn.addElement(temp);
}
//COM.claymoresystems.cert (puretls.jar)
returnCertRequest.makeSimpleDN(tdn);
}
/* Cert/key creation */
public void generateSelfSignedCertAndKey(Stringsubject, int bits,
StringPwd, StringWriter swKey, StringWriter swCert )
throws NoSuchAlgorithmException,Exception
{
X509Name_subject = makeCertDN(subject);
System.out.println("generateSelfSignedCertAndKeyCert subject: " + _subject.getNameString() +
"Strength=" + bits + " Pwd=" + Pwd);
//Generate A Cert RQ
//StringWritersw = new StringWriter(); // wil contain the priv key PEM
BufferedWriterbw = new BufferedWriter(swKey);
KeyPairkp = CertRequest.generateKey("RSA", bits, Pwd, bw, true);
//certs are valid for 1 year: 31536000 secs
byte[]certBytes = CertRequest.makeSelfSignedCert(kp, _subject, 31536000);
BufferedWriterbw1 = new BufferedWriter(swCert);
String_certPEM = writePEM(certBytes,
"-----BEGINCERTIFICATE-----\n",
"-----ENDCERTIFICATE-----\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 {
//Propertiesargs = com.ibm.grid.ogsa.securityextension.gsi.GSIProperties.load();
Stringmsg = "";
if( action.equalsIgnoreCase("GENERATE") ) {
Stringcn = request.getParameter("txtCN");
String org = request.getParameter("txtORG");
Stringou = request.getParameter("txtOU");
Stringpwd = request.getParameter("txtPWD");
Stringsubject = "O=" + org + "," + "OU="+ ou +",CN=" + cn;
System.out.println("Gencerts. Subject: " + subject);
StringWriterswCert = new StringWriter();// wil contain cert PEM
StringWriterswKey = new StringWriter();// wil contain the priv key PEM
generateSelfSignedCertAndKey(subject,1024, pwd, swKey, swCert);
//Private key
System.out.println("Privatekey PEM\n" + swKey.toString());
//cert
System.out.println("CertificatePEM\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());
}
%>
<META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<META name="GENERATOR" content="IBM WebSphere Studio">
<META http-equiv="Content-Style-Type" content="text/css">
<LINK href="../../theme/Master.css" rel="stylesheet" type="text/css">
<TITLE>setup-gsi.jsp</TITLE>
<SCRIPT LANGUAGE=javascript>
<!--
function showMsg(msg){
window.status = msg;
}
function OnLoad() {
document.F1.txtCN.focus();
showMsg("Done");
}
// fires when create btn is pressed
function O
nSubmit() {
var f = document.F1;
if ( f.txtCN.value == ""|| f.txtPWD.value == "" || f.txtOU.value == "" ) {
alert("Allfields are required");
f.txtCN.focus();
returnfalse;
}
showMsg("Working.Please wait...");
if ( document.all )
document.F1.btnCreate.disabled= true; // IE only
document.F1.submit(); //submit
}
//-->
</SCRIPT>
</HEAD>
<BODY onload="OnLoad()">
<H1>Setup Certificate/Private key</H1>
<hr>
<% if ( Msg != null ) { %>
<br><font color=red><%=Msg%></font><br>
<% } %>
<B>All fileds arerequired</B>
<form name="F1"method="POST" action="test-creds.jsp?ACTION=GENERATE"onsubmit="return OnSubmit()">
<TABLEwidth="100%" class="smallfont">
<tr>
<td>CommonName</td>
<tdwidth="40%">
<INPUTtype="text" name="txtCN" size="20" maxlength="50">
</td>
<TD>Enteryour name or email</TD>
</tr>
<tr>
<td>OrganizationalUnit</td>
<td>
<INPUTtype="text" name="txtOU" size="20" maxlength="50">
</td>
<TDclass="smallfont"></TD>
</tr>
<tr>
<td>Organization</td>
<td>
<INPUTtype="text" name="txtORG" size="20" maxlength="50">
</td>
<TDclass="smallfont">Organization</TD>
</tr>
<tr>
<td>Passphrase</td>
<td>
<INPUTtype="password" name="txtPWD" size="20" maxlength="50">
</td>
<TDclass="smallfont">Password used to encrypt the private key</TD>
</tr>
</TABLE>
<hr>
<INPUTname="btnCreate" type="submit" name="Submit"value="Install Certs">
</form>
</BODY>
</HTML>
|
这段代码在您的 Web 浏览器上会这样显示:
图 3. 设置证书/私钥
当尝试创建认证/私钥时,WebSphere 将会抛出下面的异常:
清单 4. 由 WebSphere 和 GT3 之间发生的安全提供者冲突所抛出的异常
java.lang.InternalError: java.security.NoSuchAlgorithmException: classconfigured for Cipher:
com.ibm.crypto.provider.DESedeCipheris 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 nota 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 容器之间可能会遇到的不兼容性问题。
参考资料
关于作者  | |  |
Vladimir Silva 出生在厄瓜多尔的首都基多。他于1994年在 Polytechnic Institute of the Army 获得系统分析师学位。同年,他作为交换学生来到美国,在 Middle Tennessee State University 攻读计算机科学学位。毕业之后,他加入了 IBM“Web-Ahead”技术智囊团。他兴趣广泛,包括网格计算、神经网络以及人工智能。他还拥有众多的 IT 认证,其中包括 OCP、MCSD 和 MCP。为了与 Vladimir 联系或者想要获得本文所述的
puretls.jar 和
cryptix32.jar 的修改版本, 您可以通过
vsilva@us.ibm.com把信息发给他。
|
对本文的评价
|  |