nl.uva.vlet.grid.voms.VOMSAttributeCertificate.java Source code

Java tutorial

Introduction

Here is the source code for nl.uva.vlet.grid.voms.VOMSAttributeCertificate.java

Source

/*
 * Copyright 2006-2011 The Virtual Laboratory for e-Science (VL-e) 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License").  
 * You may not use this file except in compliance with the License. 
 * For details, see the LICENCE.txt file location in the root directory of this 
 * distribution or obtain the Apache Licence at the following location: 
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 * 
 * See: http://www.vl-e.nl/ 
 * See: LICENCE.txt (located in the root folder of this distribution). 
 * ---
 * $Id: VOMSAttributeCertificate.java,v 1.4 2011-05-03 15:02:47 ptdeboer Exp $  
 * $Date: 2011-05-03 15:02:47 $
 */
// source: 
package nl.uva.vlet.grid.voms;

import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1Set;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.DERBoolean;
import org.bouncycastle.asn1.DEREncodable;
import org.bouncycastle.asn1.DEREncodableVector;
import org.bouncycastle.asn1.DERGeneralString;
import org.bouncycastle.asn1.DERGeneralizedTime;
import org.bouncycastle.asn1.DERIA5String;
import org.bouncycastle.asn1.DERInteger;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.asn1.DERPrintableString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERSet;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.AttCertIssuer;
import org.bouncycastle.asn1.x509.AttCertValidityPeriod;
import org.bouncycastle.asn1.x509.AttributeCertificate;
import org.bouncycastle.asn1.x509.AttributeCertificateInfo;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.Holder;
import org.bouncycastle.asn1.x509.IssuerSerial;
import org.bouncycastle.asn1.x509.V2Form;
import org.bouncycastle.asn1.x509.X509Extension;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.globus.gsi.OpenSSLKey;
import org.globus.gsi.bc.BouncyCastleOpenSSLKey;

//--------------------------------------------------------------------------------
/**
 * VOMSAttributeCertificate Class.
 *
 * Original Author: Gidon Moont Imperial College London Copyright (C) 2006
 * Adjustments: Piter T. de Boer University of Amsterdam 2007 - 2010
 */
public class VOMSAttributeCertificate {

    private AttributeCertificate ac = null;
    //------------------------------------------------------------------------------
    // first level contains three parts
    private AttributeCertificateInfo acinfo = null;
    private AlgorithmIdentifier signatureAlgorithm = null;
    private DERBitString signatureValue = null;
    //------------------------------------------------------------------------------
    // second level is the acinfo - this has 9 subparts
    private DERInteger version = null;
    private Holder holder = null;
    private AttCertIssuer issuer = null;
    private AlgorithmIdentifier signature = null;
    private DERInteger serialNumber = null;
    private AttCertValidityPeriod attrCertValidityPeriod = null;
    private ASN1Sequence attributes = null;
    private DERBitString issuerUniqueID = null;
    private X509Extensions extensions = null;

    //------------------------------------------------------------------------------
    public VOMSAttributeCertificate(AttributeCertificate ac) {
        this.ac = ac;

        //----------------------------------------------------------------------------
        // first level

        acinfo = ac.getAcinfo();
        signatureAlgorithm = ac.getSignatureAlgorithm();
        signatureValue = ac.getSignatureValue();

        //----------------------------------------------------------------------------
        // second level therefore is the acinfo - this has 9 subparts

        version = acinfo.getVersion();
        holder = acinfo.getHolder();
        issuer = acinfo.getIssuer();
        signature = acinfo.getSignature();
        serialNumber = acinfo.getSerialNumber();
        attrCertValidityPeriod = acinfo.getAttrCertValidityPeriod();
        attributes = acinfo.getAttributes();
        issuerUniqueID = acinfo.getIssuerUniqueID(); // OPTIONAL
        extensions = acinfo.getExtensions(); // OPTIONAL

    }

    //------------------------------------------------------------------------------
    public VOMSAttributeCertificate(String holderString, int holderSerialNumber, String issuerString,
            int productionSerial, long fromEpoch, long toEpoch, String[] fqans) throws Exception {
        try {
            DEREncodableVector infoVector = new ASN1EncodableVector();

            this.setVersion();
            this.setHolder(holderString, holderSerialNumber);
            this.setIssuer(issuerString);
            this.setAlgorithmIdentifier();
            this.setSerialNumber(productionSerial);
            this.setTimes(new Date(fromEpoch), new Date(toEpoch));
            this.setVOMSFQANs(fqans);
            this.setExtensions();

            infoVector.add(version);
            infoVector.add(holder);
            infoVector.add(issuer);
            infoVector.add(signature);
            infoVector.add(serialNumber);
            infoVector.add(attrCertValidityPeriod);
            infoVector.add(attributes);
            infoVector.add(extensions);

            ASN1Sequence infoSequence = ASN1Sequence.getInstance(new DERSequence(infoVector));

            this.acinfo = new AttributeCertificateInfo(infoSequence);

            // Do it this way to match Vincenzo as much as possible
            // - rather than this way... this.signatureAlgorithm = new AlgorithmIdentifier( "1.2.840.113549.1.1.4" ) ;
            this.signatureAlgorithm = new AlgorithmIdentifier(new DERObjectIdentifier("1.2.840.113549.1.1.4"),
                    (DEREncodable) null);

            this.signatureValue = new DERBitString(this.sign());

            this.ac = new AttributeCertificate(acinfo, signatureAlgorithm, signatureValue);

        } catch (Exception e) {
            // inspect?: 
            throw e;
        }

    }

    //------------------------------------------------------------------------------
    public AttributeCertificate getAttributeCertificate() {
        return this.ac;
    }

    //------------------------------------------------------------------------------
    public boolean verify() throws Exception {
        boolean checked = false;
        return false;

        // 
        // Piter T. de Boer: Following code does not work:
        //

        /*
         String serverName = "unknown" ;
         serverName = "voms.grid.sara.nl" ;
            
         if( ! serverName.equals( "unknown" ) )
         {
         try
         {
            
         ByteArrayOutputStream b = new ByteArrayOutputStream() ;
         new DEROutputStream( b ).writeObject( acinfo ) ;
            
         Signature sig = Signature.getInstance( signatureAlgorithm.getObjectId().getId() ) ;
            
         String vomsServerCredentialLocation = new String("c://data/.globus/"  +  serverName + ".1304") ;
            
         X509Certificate vomsServerCredential = CertUtil.loadCertificate( vomsServerCredentialLocation ) ;
            
         PublicKey pk = vomsServerCredential.getPublicKey() ;
            
         if( pk != null )
         {
         sig.initVerify( pk ) ;
         sig.update( b.toByteArray() ) ;
         if( sig.verify( signatureValue.getBytes() ) )
         {
         checked = true ;
         }
         }
            
         } 
            
            
         }
         // myLogger.warn("VOMESAttributeCertificate verification not implemented yet.");
            
         return checked ;
         */
    }

    public byte[] sign() throws Exception {
        try {

            ByteArrayOutputStream b = new ByteArrayOutputStream();
            new DEROutputStream(b).writeObject(acinfo);

            Signature sig = Signature.getInstance(signatureAlgorithm.getObjectId().getId());

            String hostPrivateKeyLocation = new String(
                    System.getProperty("user.home") + "/gridsecurity/hostkey.pem");

            OpenSSLKey key = new BouncyCastleOpenSSLKey(hostPrivateKeyLocation);

            PrivateKey pk = key.getPrivateKey();

            if (pk != null) {
                sig.initSign(pk);
                sig.update(b.toByteArray());
                byte[] sigBytes = sig.sign();
                return sigBytes;
            }

        } catch (Exception e) {
            throw e;
        }

        return new byte[0];
    }

    //------------------------------------------------------------------------------
    public BigInteger getVersion() {
        return this.version.getValue();
    }

    // always value of 1 so do not provide an option...
    public void setVersion() {
        this.version = new DERInteger(BigInteger.valueOf(1));
    }

    //------------------------------------------------------------------------------
    public String getHolder() throws Exception {

        //----------------------------------------------------------------------------
        // return the holder's DN as a String

        String holderDN = "";

        try {

            IssuerSerial baseCertificateID = this.holder.getBaseCertificateID();

            if (baseCertificateID != null) {

                GeneralName[] holder_name_array = baseCertificateID.getIssuer().getNames();
                DERSequence holder_name_sequence = (DERSequence) holder_name_array[0].getName();

                holderDN = this.DERSequencetoDN(holder_name_sequence);

            }

        } catch (Exception e) {
            throw e;
        }

        return holderDN;

    }

    public void setHolder(String holderDN, int holderSerialNumber) throws Exception {
        try {
            DERSequence holder_name_sequence = DNtoDERSequence(holderDN);

            IssuerSerial baseCertificateID = new IssuerSerial(
                    new GeneralNames(new GeneralName(4, holder_name_sequence)), new DERInteger(holderSerialNumber));

            this.holder = new Holder(baseCertificateID);
        } catch (Exception e) {
            throw e;
        }

    }

    //------------------------------------------------------------------------------
    public String getIssuer() throws Exception {
        //----------------------------------------------------------------------------
        // return the issuer's DN as a String

        String issuerDN = "";

        try {

            V2Form v2form = (V2Form) this.issuer.getIssuer();

            if (v2form != null) {
                GeneralName[] issuer_name_array = v2form.getIssuerName().getNames();
                DERSequence issuer_name_sequence = (DERSequence) issuer_name_array[0].getName();

                issuerDN = this.DERSequencetoDN(issuer_name_sequence);
            }
        } catch (Exception e) {
            throw e;
        }

        return issuerDN;
    }

    public void setIssuer(String issuerDN) throws Exception {
        try {
            DERSequence issuer_name_sequence = DNtoDERSequence(issuerDN);

            V2Form v2form = new V2Form(new GeneralNames(new GeneralName(4, issuer_name_sequence)));

            this.issuer = new AttCertIssuer(v2form);
        } catch (Exception e) {
            throw e;
        }
    }

    //------------------------------------------------------------------------------
    public String getAlgorithmIdentifier() {
        return Translate_OID.getStringFromOID(this.signature.getObjectId().getId());
    }

    // should always be "MD5 with RSA encryption"
    public void setAlgorithmIdentifier() {
        // Do it this way to match Vincenzo as much as possible
        // - rather than this way... this.signature = new AlgorithmIdentifier( "1.2.840.113549.1.1.4" ) ;
        this.signature = new AlgorithmIdentifier(new DERObjectIdentifier("1.2.840.113549.1.1.4"),
                (DEREncodable) null);
    }

    //------------------------------------------------------------------------------
    public DERInteger getSerialNumber() {
        return this.serialNumber;
    }

    public int getSerialNumberIntValue() {
        // the getValue() function of DERInteger returns a BigInteger - for which we use intValue to get the int
        return this.serialNumber.getValue().intValue();
    }

    public void setSerialNumber(int serial) {
        serialNumber = new DERInteger(serial);
    }

    //------------------------------------------------------------------------------
    public long getTime() throws Exception {
        try {
            Date from = this.attrCertValidityPeriod.getNotBeforeTime().getDate();
            Date to = this.attrCertValidityPeriod.getNotAfterTime().getDate();
            Date now = new Date();

            //TODO check this now.after( from ) thing. I always get a now which is before the from date... ???
            //      if( now.after( from ) )
            //      {
            if (now.before(to)) {
                long milliseconds_left = to.getTime() - now.getTime();
                return milliseconds_left;
            } else {
                return -1;
            }
            //      } else {
            //        return -2 ;
            //      }

        } catch (Exception e) {
            throw e;
        }
    }

    public void setTimes(Date from, Date to) throws Exception {
        try {
            this.attrCertValidityPeriod = new AttCertValidityPeriod(new DERGeneralizedTime(from),
                    new DERGeneralizedTime(to));
        } catch (Exception e) {
            throw e;
        }
    }

    //------------------------------------------------------------------------------
    public ArrayList<String> getVOMSFQANs() throws Exception {
        ArrayList<String> theseFQANs = new ArrayList<String>();
        try {
            // could have more than one AC in here...
            for (Enumeration a = this.attributes.getObjects(); a.hasMoreElements();) {

                ASN1Sequence sequence = (ASN1Sequence) a.nextElement();
                // sequence contains the OID [voms 4] (as a DERObjectIdentifier) at address 0 , and an SET at address 1

                ASN1Set set = (ASN1Set) sequence.getObjectAt(1);
                // set contains only a SEQUENCE at address 0

                ASN1Sequence sequence2 = (ASN1Sequence) set.getObjectAt(0);
                // sequence2 contains a TAGGED OBJECT ad address 0 and another SEQUENCE at address 1

                ASN1TaggedObject taggedObject = (ASN1TaggedObject) sequence2.getObjectAt(0);
                // dig down the tagged object... (undocumented?) - TagNumber value is 0

                ASN1TaggedObject taggedObject2 = (ASN1TaggedObject) taggedObject.getObject();
                // this tagged object has TagNumber value of 6 (?)
                ASN1OctetString originOctetString = (ASN1OctetString) taggedObject2.getObject();
                String origin = (new DERGeneralString(originOctetString.getOctets())).getString();

                ASN1Sequence fqanSequence = (ASN1Sequence) sequence2.getObjectAt(1);
                // this is the actual sequence of FQANs

                for (int fqan = 0; fqan < fqanSequence.size(); fqan++) {
                    ASN1OctetString fqanOctetString = (ASN1OctetString) fqanSequence.getObjectAt(fqan);
                    String FQAN_Value = (new DERGeneralString(fqanOctetString.getOctets())).getString();
                    theseFQANs.add(FQAN_Value);
                }
            }
        } catch (Exception e) {
            throw e;
        }

        return theseFQANs;
    }

    public void setVOMSFQANs(String[] fqans) throws Exception {
        try {
            //--------------------------------------------------------------------------
            // put the FQANs into the SEQUENCE

            DEREncodableVector fqanVector = new ASN1EncodableVector();

            for (int f = 0; f < fqans.length; f++) {
                DERGeneralString fqan = new DERGeneralString(fqans[f]);
                ASN1OctetString fqanOctetString = ASN1OctetString.getInstance(new DEROctetString(fqan.getOctets()));
                fqanVector.add(fqanOctetString);
            }

            ASN1Sequence fqanSequence = ASN1Sequence.getInstance(new DERSequence(fqanVector));

            //--------------------------------------------------------------------------
            // put something into the undocumented TaggedObject

            DERGeneralString origin = new DERGeneralString("gridportal://newvoms:15000");

            ASN1OctetString originOctetString = ASN1OctetString.getInstance(new DEROctetString(origin.getOctets()));

            /*
             ASN1TaggedObject taggedObject2 = ASN1TaggedObject.getInstance( new DERTaggedObject( 6 , originOctetString ) , true ) ;
                
             ASN1TaggedObject taggedObject = ASN1TaggedObject.getInstance( new DERTaggedObject( 0 , taggedObject2 ) , true ) ;
                
             DEROctetString originOctetString = new DEROctetString( origin.getOctets() ) ;
             */

            DERTaggedObject taggedObject2 = new DERTaggedObject(6, originOctetString);

            DERTaggedObject taggedObject = new DERTaggedObject(0, taggedObject2);

            //--------------------------------------------------------------------------
            // put the taggedObject and then the fqanSequence into sequence2

            DEREncodableVector sequence2Vector = new ASN1EncodableVector();
            sequence2Vector.add(taggedObject);
            sequence2Vector.add(fqanSequence);
            ASN1Sequence sequence2 = ASN1Sequence.getInstance(new DERSequence(sequence2Vector));

            //--------------------------------------------------------------------------
            // the SET has one member - sequence2

            ASN1Set set = ASN1Set.getInstance(new DERSet(sequence2));

            //--------------------------------------------------------------------------
            // SEQUENCE sequence has an OID and the set

            DERObjectIdentifier voms4oid = new DERObjectIdentifier("1.3.6.1.4.1.8005.100.100.4");

            DEREncodableVector sequenceVector = new ASN1EncodableVector();
            sequenceVector.add(voms4oid);
            sequenceVector.add(set);
            ASN1Sequence sequence = ASN1Sequence.getInstance(new DERSequence(sequenceVector));

            //--------------------------------------------------------------------------

            this.attributes = ASN1Sequence.getInstance(new DERSequence(sequence));

        } catch (Exception e) {
            throw e;
        }

    }

    //------------------------------------------------------------------------------
    // Extensions
    // DOCUMENTATION REQUIRED!!
    /*
      Current (October 2006) VOMS ACs always have id-ce-noRevAvail and  Authority Key Identifier 
      Extensions
        
      The id-ce-noRevAvail is set to NULL
        
      The AuthorityKeyIdentifier object.
      id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::=  { id-ce 35 }
        
      AuthorityKeyIdentifier ::= SEQUENCE {
      keyIdentifier             [0] IMPLICIT KeyIdentifier           OPTIONAL,
      authorityCertIssuer       [1] IMPLICIT GeneralNames            OPTIONAL,
      authorityCertSerialNumber [2] IMPLICIT CertificateSerialNumber OPTIONAL  }
        
      KeyIdentifier ::= OCTET STRING
        
      Vincenzo is using a 160-bit SHA-1 hash of the value of the BIT STRING subjectPublicKey (as described in 4.2.1.2 of RFC 2459)
        
      */
    private void setExtensions() throws Exception {
        try {
            Vector<DERObjectIdentifier> myOIDs = new Vector<DERObjectIdentifier>();
            Hashtable<DERObjectIdentifier, X509Extension> myExtensions = new Hashtable<DERObjectIdentifier, X509Extension>();

            //--------------------------------------------------------------------------
            // id-ce-noRevAvail

            ByteArrayOutputStream a = new ByteArrayOutputStream();
            new DEROutputStream(a).writeObject((new DERNull()).toASN1Object());
            ASN1OctetString nraOctetString = ASN1OctetString.getInstance(new DEROctetString(a.toByteArray()));

            X509Extension nraExtension = new X509Extension(new DERBoolean(false), nraOctetString);
            DERObjectIdentifier nraOID = new DERObjectIdentifier("2.5.29.56");

            myOIDs.add(nraOID);
            myExtensions.put(nraOID, nraExtension);

            //--------------------------------------------------------------------------
            // AuthorityKeyIdentifier
            //            myLogger.warn("VOMSAttributeCertificate verification not implemented yet.");

            //      String issuerDN = this.getIssuer() ;
            //
            //      String serverName = "unknown" ;
            //      //serverName = VirtualOrganisation.getServer( issuerDN ) ;
            //
            //      if( ! serverName.equals( "unknown" ) )
            //      {
            //        String vomsServerCredentialLocation = new String( System.getProperty( "user.home" ) + "/gridsecurity/certificates/voms-server-certificates/" + serverName ) ;
            //
            //        X509Certificate vomsServerCredential = CertUtil.loadCertificate( vomsServerCredentialLocation ) ;
            //
            //        PublicKey pk = vomsServerCredential.getPublicKey() ;
            //
            //        SubjectPublicKeyInfo spki = new SubjectPublicKeyInfo( (ASN1Sequence) new ASN1InputStream( new ByteArrayInputStream( pk.getEncoded() ) ).readObject() ) ;
            //        AuthorityKeyIdentifier aki = new AuthorityKeyIdentifier( spki ) ;
            //
            //        // not clear why this does not work...
            //        // DEROctetString akiOctetString = (DEROctetString) DEROctetString.getInstance( akiSequence.getDERObject() ) ;
            //
            //        // These three lines get to the desired result...
            //        ByteArrayOutputStream b = new ByteArrayOutputStream() ;
            //        new DEROutputStream( b ).writeObject( aki.toASN1Object() ) ; 
            //        ASN1OctetString akiOctetString = ASN1OctetString.getInstance( new DEROctetString( b.toByteArray() ) ) ;
            //
            //        X509Extension akiExtension = new X509Extension( new DERBoolean( false ) , akiOctetString ) ;
            //        DERObjectIdentifier akiOID = new DERObjectIdentifier( "2.5.29.35" ) ;
            //
            //        myOIDs.add( akiOID ) ;
            //        myExtensions.put( akiOID , akiExtension ) ;
            //
            //        this.extensions = new X509Extensions( myOIDs , myExtensions ) ;
            //
            //      }

        } catch (Exception e) {
            //            myLogger.error(null, e);
            //e.printStackTrace() ; 
        }

    }

    //------------------------------------------------------------------------------
    private String DERSequencetoDN(DERSequence this_sequence) throws Exception {
        String thisDN = "";

        try {

            for (Enumeration n = this_sequence.getObjects(); n.hasMoreElements();) {
                DERSet this_set = (DERSet) n.nextElement();
                DERSequence this_seq = (DERSequence) this_set.getObjectAt(0);
                try {
                    DERPrintableString this_string = (DERPrintableString) this_seq.getObjectAt(1);
                    thisDN = thisDN.concat("/" + Translate_OID.getStringFromOID("" + this_seq.getObjectAt(0)) + "="
                            + this_string.getString());
                } catch (Exception notPS) {
                    // email is encoded differently?
                    DERIA5String this_string = (DERIA5String) this_seq.getObjectAt(1);
                    thisDN = thisDN.concat("/" + Translate_OID.getStringFromOID("" + this_seq.getObjectAt(0)) + "="
                            + this_string.getString());
                }
            }

        } catch (Exception e) {
            throw e;
        }

        return thisDN;
    }

    //------------------------------------------------------------------------------
    private DERSequence DNtoDERSequence(String thisDN) throws Exception {
        DERSequence this_sequence = null;

        try {
            DEREncodableVector this_overall_vector = new ASN1EncodableVector();

            String[] parts = thisDN.split("/");

            for (int p = 1; p < parts.length; p++) {

                int equals_position = parts[p].indexOf("=");

                String oid_string = parts[p].substring(0, equals_position);
                String value_string = parts[p].substring(equals_position + 1);
                String oid = Translate_OID.getOIDFromString(oid_string);
                if (oid.equals(oid_string)) {
                    throw new Exception("unrecognised OID string :: " + oid);
                }

                DEREncodableVector this_vector = new ASN1EncodableVector();
                DERObjectIdentifier this_oid = new DERObjectIdentifier(oid);

                this_vector.add(this_oid);

                if (oid_string.equals("E")) {
                    DERIA5String this_string = new DERIA5String(value_string);
                    this_vector.add(this_string);
                } else {
                    DERPrintableString this_string = new DERPrintableString(value_string);
                    this_vector.add(this_string);
                }

                DERSet this_single_object_set = new DERSet(new DERSequence(this_vector));

                this_overall_vector.add(this_single_object_set);

            }

            this_sequence = new DERSequence(this_overall_vector);

        } catch (Exception e) {
            throw e;
        }

        return this_sequence;

    }
    //------------------------------------------------------------------------------
}