/* This class is a rewrite of the classes VOMSClient and
* MyGloubsCredentialUtils
*
* Gidon Moont from
* Imperial College London
*
* wrote. I did not change the functionality, just some things like
* logging to use it better with grix.
* So: all the credit goes to Gidon.
*/
package grith.jgrith.vomsProxy;
import gridpp.portal.voms.VOMSAttributeCertificate;
import gridpp.portal.voms.VincenzoBase64;
import grisu.jcommons.exceptions.CredentialException;
import grisu.model.info.dto.VO;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.security.GeneralSecurityException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Enumeration;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.x509.AttributeCertificate;
import org.globus.gsi.GSIConstants;
import org.globus.gsi.GlobusCredential;
import org.globus.gsi.X509ExtensionSet;
import org.globus.gsi.bc.BouncyCastleCertProcessingFactory;
import org.globus.gsi.bc.BouncyCastleX509Extension;
import org.globus.gsi.gssapi.GSSConstants;
import org.globus.gsi.gssapi.GlobusGSSCredentialImpl;
import org.globus.gsi.gssapi.GlobusGSSManagerImpl;
import org.globus.gsi.gssapi.auth.Authorization;
import org.globus.gsi.gssapi.auth.IdentityAuthorization;
import org.globus.gsi.gssapi.net.GssSocket;
import org.globus.gsi.gssapi.net.GssSocketFactory;
import org.gridforum.jgss.ExtendedGSSContext;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The actual credential that is build of a GlobusCredential and an
* AttributeCertificate. The AttributeCertificate is sent by the VOMS server
* after sending one of this commands:
*
* <br>
* A - this means get everything the server knows about you <br>
* G/group - This means get group informations. /group should be /vo-name. <br>
* This is the default request used by voms-proxy-init <br>
* Rrole - This means grant me the specified role, in all groups in which <br>
* you can grant it. <br>
* Bgroup:role - This means grant me the specified role in the specified group.
*
* Most of the code that does the voms magic is written by Gidon Moont from
* Imperial College London, http://gridportal.hep.ph.ic.ac.uk/
*
*/
public class VomsProxyCredential {
static final Logger myLogger = LoggerFactory.getLogger(VomsProxyCredential.class
.getName());
/**
* Static method that returns all included AttributesCertificates of a
* GlobusCredential. In general we are only interested in the first one.
*
* @param vomsProxy
* the voms enabled proxy credential
* @return all AttributeCertificates
*/
public static ArrayList<AttributeCertificate> extractVOMSACs(
GlobusCredential vomsProxy) {
// the aim of this is to retrieve all VOMS ACs
ArrayList<AttributeCertificate> acArrayList = new ArrayList<AttributeCertificate>();
try {
X509Certificate[] x509s = vomsProxy.getCertificateChain();
for (X509Certificate x509 : x509s) {
try {
byte[] payload = x509
.getExtensionValue("1.3.6.1.4.1.8005.100.100.5");
// Octet String encapsulation - see RFC 3280 section 4.1
payload = ((ASN1OctetString) new ASN1InputStream(
new ByteArrayInputStream(payload)).readObject())
.getOctets();
ASN1Sequence acSequence = (ASN1Sequence) new ASN1InputStream(
new ByteArrayInputStream(payload)).readObject();
for (Enumeration e1 = acSequence.getObjects(); e1
.hasMoreElements();) {
ASN1Sequence seq2 = (ASN1Sequence) e1.nextElement();
for (Enumeration e2 = seq2.getObjects(); e2
.hasMoreElements();) {
AttributeCertificate ac = new AttributeCertificate(
(ASN1Sequence) e2.nextElement());
acArrayList.add(ac);
}
}
} catch (Exception pe) {
// System.out.println( "This part of the chain has no AC" )
// ;
}
}
} catch (Exception e) {
// e.printStackTrace();
myLogger.error("Could not extract AttributeCertificate.", e);
}
return acArrayList;
}
private GlobusCredential plainProxy = null;
private GlobusCredential vomsProxy = null;
private AttributeCertificate ac = null;
private VOMSAttributeCertificate vomsac = null;
private String command = null;
private String order = null;
private long lifetime = -1;
private VO vo = null;
/**
* Don't use this.
*
* @throws Exception
*
* @throws Exception
*/
// public VomsProxyCredential() throws Exception {
// this(LocalProxy.getDefaultProxy().getGlobusCredential(), VO
// .getDefaultVO(), "G/" + VO.getDefaultVO().getVoName(), 10000);
// }
public VomsProxyCredential(GlobusCredential vomsProxy) throws CredentialException {
this.vomsProxy = vomsProxy;
// try to extract the first attribute credential (hopefully this is the
// voms one
ArrayList<AttributeCertificate> acs = VomsProxyCredential.extractVOMSACs(vomsProxy);
if ( acs.size() == 0 ) {
throw new CredentialException("Credential is not voms enabled.");
}
ac = acs.get(0);
// if (ac == null) {
// throw new Exception(
// "Could not extract Voms attribute certificate from this globus credential. Probably this is not a voms proxy.");
// }
vomsac = new VOMSAttributeCertificate(ac);
}
public VomsProxyCredential(GlobusCredential gridProxy,
long lifetime_in_seconds, VO vo, String command, String order)
throws Exception {
this.plainProxy = gridProxy;
this.vo = vo;
this.command = command;
this.order = order;
this.lifetime = lifetime_in_seconds;
getAC();
generateProxy();
vomsac = new VOMSAttributeCertificate(ac);
}
public VomsProxyCredential(GlobusCredential gridProxy, VO vo,
String command, String order) throws Exception {
this(gridProxy, gridProxy.getTimeLeft(), vo, command, order);
}
/**
* The default constructor. Assembles a VomsProxyCredential.
*
* @deprecated Don't use this constructor anymore. Use the one that needs
* seconds for lifetime...
*
* @param gridProxy
* a X509 proxy (can be the local proxy or a myproxy proxy
* credential.
* @param vo
* the VO
* @param command
* the command to send to the VOMS server
* @param lifetime_in_hours
* the lifetime of the proxy in hours
* @param order
* the order
* @throws Exception
* if something fails, obviously
*/
@Deprecated
public VomsProxyCredential(GlobusCredential gridProxy, VO vo,
String command, String order, int lifetime_in_hours)
throws Exception {
this.plainProxy = gridProxy;
this.vo = vo;
this.command = command;
this.order = order;
this.lifetime = lifetime_in_hours * 3600;
getAC();
generateProxy();
vomsac = new VOMSAttributeCertificate(ac);
}
public void destroy() {
plainProxy = null;
vomsProxy = null;
ac = null;
}
private void generateProxy() throws GeneralSecurityException {
// Extension 1
DERSequence seqac = new DERSequence(this.ac);
DERSequence seqacwrap = new DERSequence(seqac);
BouncyCastleX509Extension ace = new BouncyCastleX509Extension(
"1.3.6.1.4.1.8005.100.100.5", seqacwrap);
// Extension 2
// KeyUsage keyUsage = new KeyUsage(KeyUsage.digitalSignature
// | KeyUsage.keyEncipherment | KeyUsage.dataEncipherment);
// BouncyCastleX509Extension kue = new BouncyCastleX509Extension(
// "2.5.29.15", keyUsage.getDERObject());
// Extension Set
X509ExtensionSet globusExtensionSet = new X509ExtensionSet();
globusExtensionSet.add(ace);
// globusExtensionSet.add(kue);
// generate new VOMS proxy
BouncyCastleCertProcessingFactory factory = BouncyCastleCertProcessingFactory
.getDefault();
vomsProxy = factory.createCredential(plainProxy.getCertificateChain(),
plainProxy.getPrivateKey(), plainProxy.getStrength(),
(int) plainProxy.getTimeLeft(), GSIConstants.DELEGATION_FULL,
globusExtensionSet);
}
/**
* Contacts the VOMS server to get an AttributeCertificate
*
* @return true if successful, false if not
* @throws GSSException
* @throws IOException
*/
private boolean getAC() throws GSSException, IOException {
boolean success = false;
int server = 0;
myLogger.debug("Contacting VOMS server [" + vo.getHost()
+ "] with command: " + command);
GSSManager manager = new GlobusGSSManagerImpl();
Authorization authorization = new IdentityAuthorization(vo.getHostDN());
GSSCredential clientCreds = new GlobusGSSCredentialImpl(
plainProxy, GSSCredential.INITIATE_ONLY);
ExtendedGSSContext context = (ExtendedGSSContext) manager
.createContext(null, GSSConstants.MECH_OID, clientCreds,
GSSContext.DEFAULT_LIFETIME);
context.requestMutualAuth(true);
context.requestCredDeleg(false);
context.requestConf(true);
context.requestAnonymity(false);
context.setOption(GSSConstants.GSS_MODE, GSIConstants.MODE_GSI);
context.setOption(GSSConstants.REJECT_LIMITED_PROXY, new Boolean(false));
GssSocket socket = (GssSocket) GssSocketFactory.getDefault()
.createSocket(vo.getHost(), vo.getPort(), context);
socket.setWrapMode(GssSocket.GSI_MODE);
socket.setAuthorization(authorization);
OutputStream out = ((Socket) socket).getOutputStream();
InputStream in = ((Socket) socket).getInputStream();
String msg = null;
if ((order == null) || "".equals(order)) {
msg = new String(
"<?xml version=\"1.0\" encoding = \"US-ASCII\"?><voms><command>"
+ command + "</command><lifetime>" + lifetime
+ "</lifetime></voms>");
} else {
msg = new String(
"<?xml version=\"1.0\" encoding = \"US-ASCII\"?><voms><command>"
+ command + "</command><order>" + order
+ "</order><lifetime>" + lifetime
+ "</lifetime></voms>");
}
byte[] outToken = msg.getBytes();
out.write(outToken);
out.flush();
StringBuffer voms_server_answer = new StringBuffer();
BufferedReader buff = new BufferedReader(new InputStreamReader(in));
char[] buf = new char[1024];
int numRead = 0;
while ((numRead = buff.read(buf)) != -1) {
String readData = String.valueOf(buf, 0, numRead);
voms_server_answer.append(readData);
buf = new char[1024];
}
// String answer = buff.readLine();
out.close();
in.close();
buff.close();
String answer = voms_server_answer.toString();
if (answer.indexOf("<error>") > 1) {
String errormsg = answer.substring(answer.indexOf("<message>") + 9,
answer.indexOf("</message>"));
myLogger.warn("VOMS server returned an error => " + errormsg);
server++;
}
String encoded;
try {
encoded = answer.substring(answer.indexOf("<ac>") + 4,
answer.indexOf("</ac>"));
} catch (IndexOutOfBoundsException e) {
myLogger.warn("Could not find encoded voms proxy in server answer.");
return success;
}
try {
byte[] payload = VincenzoBase64.decode(encoded);
ByteArrayInputStream is = new ByteArrayInputStream(payload);
ASN1InputStream asnInStream = new ASN1InputStream(is);
ASN1Sequence acseq = (ASN1Sequence) asnInStream.readObject();
ac = new org.bouncycastle.asn1.x509.AttributeCertificate(acseq);
} catch (Exception e) {
myLogger.info(
"Could not get AttributeCertificate: {}. Probably means the user is not member of the VO.",
e.getLocalizedMessage());
throw new IOException(e);
}
success = true;
myLogger.debug("Success");
return success;
}
public AttributeCertificate getAttributeCertificate() {
return ac;
}
/**
* @return the voms enabled proxy
*/
public GlobusCredential getVomsProxy() {
return vomsProxy;
}
/**
* Gathers information in the attribute certificate into an ArrayList
*
* @return the equivalent of a commandline voms-proxy-info --all / null if
* something's not right
*/
public ArrayList<String> vomsInfo() {
ArrayList<String> info = new ArrayList<String>();
info.add("=== VO extension information ===");
try {
info.add("issuer\t\t: " + vomsac.getIssuer());
boolean checked = vomsac.verify();
if (checked) {
info.add("validity\t: ... signature is valid");
} else {
info.add("validity\t: WARNING - Unable to validate the signature of this AC - DO NOT TRUST!");
}
long milliseconds = vomsac.getTime();
if (milliseconds > 0) {
int hours = new Long(milliseconds / (1000 * 3600)).intValue();
int minutes = new Long((milliseconds - (hours * 1000 * 3600))
/ (1000 * 60)).intValue();
int seconds = new Long(
(milliseconds - ((hours * 1000 * 3600) + (minutes * 1000 * 60))) / 1000)
.intValue();
info.add("time left\t: " + hours + ":" + minutes + ":"
+ seconds);
} else {
info.add("WARNING - this AC is not within its valid time - DO NOT TRUST!");
}
info.add("holder\t\t: " + vomsac.getHolder());
info.add("version\t\t: " + vomsac.getVersion());
info.add("algorithm\t: " + vomsac.getAlgorithmIdentifier());
info.add("serialNumber\t: " + vomsac.getSerialNumberIntValue());
for (String line : vomsac.getVOMSFQANs()) {
info.add("attribute\t: " + line);
}
// info.addAll(vomsac.getVOMSFQANs());
} catch (Exception e) {
myLogger.error(e.getLocalizedMessage());
return null;
}
return info;
}
}