Our Online Store have the new products: RFID antenna board. Currently it can work with JC10M24R and JCOP4 card chips.
Compared with normal cards, the antenna board module has a smaller size and fixed holes, which is easy to integrate in the IOT(Internet Of Things) project.

parsing X509 certificate

JavaCard Applet Development Related Questions and Answers.
Joevictor
Posts: 9
Joined: Tue Oct 20, 2015 6:05 am
Points :22
Contact:

parsing X509 certificate

Post by Joevictor » Wed Jan 06, 2016 2:16 am

Hi all! Does anybody working on parsing X509 certificate? If yes, could you give me some open source code that deals with x509 certificate parsing in JavaCard compatible Java? Thanks.

Tarantino
Posts: 101
Joined: Wed Aug 19, 2015 1:56 am
Points :478
Contact:

Re: parsing X509 certificate

Post by Tarantino » Tue Jan 12, 2016 10:06 pm

Here are the code which implements methods for creating X.509 certificates and accessing their attributes.
Hope this helps!

Code: Select all

package be.msec.jorn;
  import java.io.IOException;
//import java.io.UnsupportedEncodingException;

import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import javacard.framework.Util;
import javacard.security.KeyBuilder;
import javacard.security.MessageDigest;
import javacard.security.PublicKey;
import javacard.security.RSAPublicKey;
import javacardx.crypto.Cipher;
 


 /**
  * This class implements methods for creating X.509 certificates and
  * accessing their attributes such as subject/issuer names, public keys
  * and validity information. Publicly visible methods methods are
  * modeled after those in the X509Certificate classes
  * from J2SE (standard edition) but there are some differences and
  * these are documented below. <P />
  * NOTE: For now, only X.509 certificates containing RSA public keys
  * and signed either using md2WithRSA, md5WithRSA, or sha-1WithRSA are
  * supported.
  * This version of the implementation is unable to parse certificates
  * containing DSA keys or signed using DSA. Certificates containing
  * RSA keys but signed using an unsupported algorithm
  * can be parsed but cannot be verified. Not all version 3 extensions are
  * supported (only subjectAltName, basicConstraints, keyUsage and
  * extendedKeyUsage are recognized) but if an unrecognized
  * extension is marked critical, an error notification is generated.
  * <P />
  */
 public class X509Certificate {

     /** Indicates a no error condition. */
     public static final byte NO_ERROR = 0;
     
     /** X509Certificate Errors **/
    public final static short SW_CERT_PARSE_FAILED = 0x6301;
    public final static short SW_MATCH1_FAILED = 0x6302;
    public final static short SW_MATCH2_FAILED = 0x6303;
    public final static short SW_LEN_1_ERROR = 0x6304;
    public final static short SW_LEN_2_ERROR = 0x6305;
    public final static short SW_UTCTIME_1_ERROR = 0x6306;
    public final static short SW_UTCTIME_2_ERROR = 0x6307;
    public final static short SW_UTCTIME_3_ERROR = 0x6308;
    public final static short SW_EXTENSIONS_INVALID = 0x6309;
    public final static short SW_CERT_DATA_INVALID = 0x630A;
    public final static short SW_CERT_ISSUER_INVALID = 0x630B;
    public final static short SW_CERT_SUBJECT_INVALID = 0x630C;
    public final static short SW_CERT_SIG_INVALID = 0x630D;
    
    
     /**
      * Indicates that no information is available on
      * the pathLengthConstraint associated with this certificate
      * (this could happen if the certifiate is a v1 or v2 cert or
      * a v3 cert without basicConstraints or a non-CA v3 certificate).
      */
     public static final short MISSING_PATH_LENGTH_CONSTRAINT = -1;
     /** Indicates there is no limit to the server certificate chain length. */
     public static final short UNLIMITED_CERT_CHAIN_LENGTH = 32767;

     /** We expect issuer/subject names to fit within these many bytes. */
     private static final short MAX_NAME_LENGTH = 300;

     /** ASN ANY_STRING type used in certificate parsing (0x00). */
     private static final byte ANY_STRING_TYPE = 0x00; // our own impl

     /** ASN INTEGER type used in certificate parsing (0x02). */
     private static final byte INTEGER_TYPE = 0x02;
     /** ASN BIT STRING type used in certificate parsing (0x03). */
     private static final byte BITSTRING_TYPE = 0x03;
     /** ASN OCTET STRING type used in certificate parsing (0x04). */
     private static final byte OCTETSTR_TYPE = 0x04;
     /** ASN OBJECT ID type used in certificate parsing (0x06). */
     private static final byte OID_TYPE = 0x06;
     /** ASN UTF8 STRING type used in certificate parsing (0x0c). */
     private static final byte UTF8STR_TYPE = 0x0c;
     /** ASN UNICODE STRING type used in certificate parsing (0x12). */
     private static final byte UNIVSTR_TYPE = 0x12;
     /** ASN PRINT STRING type used in certificate parsing (0x13). */
     private static final byte PRINTSTR_TYPE = 0x13;
     /** ASN TELETEX STRING type used in certificate parsing (0x14). */
     private static final byte TELETEXSTR_TYPE = 0x14;

     /** ASN IA5 STRING type used in certificate parsing (0x16). */
     private static final byte IA5STR_TYPE = 0x16; // Used for EmailAddress
     /** ASN SEQUENCE type used in certificate parsing (0x30). */
     private static final byte SEQUENCE_TYPE = 0x30;
     /** ASN SET type used in certificate parsing (0x31). */
     private static final byte SET_TYPE = 0x31;

     /** Email address (rfc 822) alternative name type code. */
     public static final byte TYPE_EMAIL_ADDRESS = 1;
     /** DNS name alternative name type code. */
     public static final byte TYPE_DNS_NAME = 2;
     /** URI alternative name type code. */
     public static final byte TYPE_URI = 6;

     /** Bit mask for digital signature key usage.  */
     public static final short DIGITAL_SIG_KEY_USAGE = 0x00000001;
     /** Bit mask for non repudiation key usage. */
     public static final short NON_REPUDIATION_KEY_USAGE = 0x00000002;
     /** Bit mask for key encipherment key usage. */
     public static final short KEY_ENCIPHER_KEY_USAGE = 0x00000004;
     /** Bit mask for data encipherment key usage. */
     public static final short DATA_ENCIPHER_KEY_USAGE = 0x00000008;
     /** Bit mask for key agreement key usage. */
     public static final short KEY_AGREEMENT_KEY_USAGE = 0x00000010;
     /** Bit mask for key certificate sign key usage. */
     public static final short CERT_SIGN_KEY_USAGE = 0x00000020;
     /** Bit mask for CRL sign key usage. */
     public static final short CRL_SIGN_KEY_USAGE = 0x00000040;
     /** Bit mask for encipher only key usage. */
     public static final short ENCIPHER_ONLY_KEY_USAGE = 0x00000080;
     /** Bit mask for decipher only key usage. */
     public static final short DECIPHER_ONLY_KEY_USAGE = 0x00000100;

     /** Bit mask server auth for extended key usage. */
     public static final short SERVER_AUTH_EXT_KEY_USAGE = 0x00000002;
     /** Bit mask client auth for extended key usage. */
     public static final short CLIENT_AUTH_EXT_KEY_USAGE = 0x00000004;
     /** Bit code signing mask for extended key usage. */
     public static final short CODE_SIGN_EXT_KEY_USAGE = 0x00000008;
     /** Bit email protection mask for extended key usage. */
     public static final short EMAIL_EXT_KEY_USAGE = 0x00000010;
     /** Bit IPSEC end system mask for extended key usage. */
     public static final short IPSEC_END_SYS_EXT_KEY_USAGE = 0x00000020;
     /** Bit IPSEC tunnel mask for extended key usage. */
     public static final short IPSEC_TUNNEL_EXT_KEY_USAGE = 0x00000040;
     /** Bit IPSEC user mask for extended key usage. */
     public static final short IPSEC_USER_EXT_KEY_USAGE = 0x00000080;
     /** Bit time stamping mask for extended key usage. */
     public static final short TIME_STAMP_EXT_KEY_USAGE = 0x00000100;

     /**
      * The validity period is contained in thirteen bytes
      * yymmddhhmmss followed by 'Z' (for zulu ie GMT), if yy < 50
      * assume 20yy else 19yy.
      */
     private static final short UTC_LENGTH = 13;

     /**
      * Maps byte codes that follow id-at (0x55 0x04) to corresponding name
      * component tags (e.g. Commom Name, or CN, is 0x55, 0x04, 0x03 and
      * Country, or C, is 0x55, 0x04, 0x06). See getName. See X.520 for
      * the OIDs and RFC 1779 for the printable labels. Place holders for
      * unknown labels have a 0 as the first byte.
      */
     private static final byte[] b1 = { 0 };
     private static final byte[] b2 = { 0 };
     private static final byte[] b3 = { 0 };
     private static final byte[] cn = { 'C', 'N' };
     private static final byte[] sn = { 'S', 'N' };
     private static final byte[] b4 = { 0 };
     private static final byte[] c = { 'C' };
     private static final byte[] l = { 'l' };
     private static final byte[] st = { 'S', 'T' };
     private static final byte[] street = { 'S', 'T', 'R', 'E', 'E', 'T' };
     private static final byte[] o = { 'O' };
     private static final byte[] ou = { 'O', 'U' };
     //private static final byte[][] nameAttr = {b1, b2, b3, cn, sn, b4, c, l , st, street, o , ou};
     
//            { 0 },
//            { 0 },
//            { 0 },
//             { 'C', 'N' }, // Common name: id-at 3
//             { 'S', 'N' }, // Surname: id-at 4
//             { 0 },
//             { 'C' }, // Country: id-at 6
//             { 'L' }, // Locality: id-at 7
//             { 'S', 'T' }, // State or province: id-at 8
//             { 'S', 'T', 'R', 'E', 'E', 'T' }, // Street address: id-at 9
//             { 'O' }, // Organization: id-at 10
//             { 'O', 'U' }, // Organization unit: id-at 11
//     };

     /** Email attribute label in bytes. "EmailAddress" */
     private static final byte[] EMAIL_ATTR_LABEL = { 'E', 'm', 'a',
             'i', 'l', 'A', 'd', 'd', 'r', 'e', 's', 's' };

     /** Email attribute object identifier. */
     private static final byte[] EMAIL_ATTR_OID = { (byte) 0x2a,
             (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7,
             (byte) 0x0d, (byte) 0x01, (byte) 0x09, (byte) 0x01 };

     /** Includes DER encoding for OID 1.2.840.113549.1.1. */
     private static final byte[] PKCS1Seq = { (byte) 0x30, (byte) 0x0d,
             (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
             (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d,
             (byte) 0x01, (byte) 0x01, };

     /*
      * These signature algorithms are encoded as PKCS1Seq followed by
      * a single byte with the corresponding value shown below, e.g.
      * md5WithRSAEncryption OBJECT IDENTIFIER  ::=  {
      *     iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1)
      *     pkcs-1(1) 4 
      * }
      */
     /** Uknown algorithm (-1). */
     private static final byte NONE = -1;
     /** RAS ENCRYPTION (0x01). */
     private static final byte RSA_ENCRYPTION = 0x01;
     
     /** SHA1_RSA algorithm (0x05). */
     private static final byte SHA1_RSA = 0x05;

     /**
      * Expected prefix in decrypted value when SHA-1 hash is used for signing
      * 30 21 30 09 06 05 2b 0e 03 02 1a 05 00 04 14.
      */
     private static final byte[] PREFIX_SHA1 = { (byte) 0x30,
             (byte) 0x21, (byte) 0x30, (byte) 0x09, (byte) 0x06,
             (byte) 0x05, (byte) 0x2b, (byte) 0x0e, (byte) 0x03,
             (byte) 0x02, (byte) 0x1a, (byte) 0x05, (byte) 0x00,
             (byte) 0x04, (byte) 0x14 };

     /** ASN encoding for NULL. */
     private static final byte[] NullSeq = { (byte) 0x05, (byte) 0x00 };

     /** This is how the encoding of validity information begins. */
     private static final byte[] ValiditySeq = { (byte) 0x30,
             (byte) 0x1e };

     /** This is how the encoding of UTCTime begins. */
     private static final byte[] UTCSeq = { (byte) 0x17, (byte) 0x0d };

     /** Includes DER encoding for id-kp (key purpose). */
     private static final byte[] ID_KP = { (byte) 0x2b, (byte) 0x06,
             (byte) 0x01, (byte) 0x05, (byte) 0x05, (byte) 0x07,
             (byte) 0x03 };

     /** True iff subject matches issuer. */
     private boolean selfSigned;
     /** X.509 version. For more readable code the version field starts a 1. */
     private byte version = 1;
     /** MD5 fingerprint of the certificate. */
     private byte[] fp = null;
     /** Certificate serial number. */
     private byte[] serialNumber;
     /** Certificate subject. */
     private byte[] subject;
     /** Certificate issuer. */
     private byte[] issuer;
     /** Beginning of certificate validity period. */
     private Date from;
     /** End of certificate validity period. */
     private Date until;
     /** Certificate RSA Public key. */
     private RSAPublicKey pubKey = null;

     /** Index inside encoding. */
     private short idx = 0;
     /** Contains Certificate DER encoding. */
     private byte[] enc = null;
     /** Offset where TBSCertificate starts. */
     private short TBSStart = 0;
     /** Length of TBSCertificate. */
     private short TBSLen = 0;
     /** Algorithm used to sign the cert. */
     private byte sigAlg = NONE;
     /** Issuer signature on certificate. */
     private byte[] signature = null;
     /**  Hash of TBSCertificate. */
     private byte[] TBSCertHash = null;
     /**  True iff cert has unrecognized critical extension. */
     private boolean badExt = false;
     /**  Alternate name. */

     /** format of the subject alternative name, 2 means a DNS name */
     private byte subAltNameType;
     /** subject alternative name */
     private Object subAltName;
     /** does the cert include BasicConstaints. */
     private boolean hasBC = false;
     /** CA value in BasicConstraints. */
     private boolean isCA = false;
     /** Path Length constriant from Basic constraints. */
     private short pLenConstr = MISSING_PATH_LENGTH_CONSTRAINT;
     /** Collection of keyUsage bits. */
     private short keyUsage = -1;
     /** Collection of extended keyUsage bits. */
     private short extKeyUsage = -1;

     /** Private constructor */
     public X509Certificate() {
     }

     /**
      * Matches the contents of buf against this certificates DER
      * encoding (enc) starting at the current offset (idx).
      * <P />
      * @param buf buffer whose contents are to be matched against the
      *            certificate encoding
      * @exception Exception if the match fails
      */
     private void match(byte[] buf) throws Exception {
         if ((short)(idx + buf.length) < (short)enc.length) {
             for (short i = 0; i < (short)buf.length; i++) {
                 if (enc[(short)(idx++)] != buf[(short)i])
                    ISOException.throwIt(SW_MATCH1_FAILED);
             }
         } else {
            ISOException.throwIt(SW_MATCH2_FAILED);
         }
     }

     /**
      * Matches the specified ASN type against this certificates DER
      * encoding (enc) starting at the current offset (idx) and returns
      * its encoded length.
      * <P />
      * @param type ASN type to be matched
      * @return the size in bytes of the sub-encoding associated with
      *         the given type
      * @exception IOException if the length is not formated correctly
      */
     private short getLen(byte type) {

         if ((enc[(short)idx] == type) || ((type == ANY_STRING_TYPE) && // ordered by likelihood of match
                 ((enc[idx] == PRINTSTR_TYPE)
                         || (enc[idx] == TELETEXSTR_TYPE)
                         || (enc[idx] == UTF8STR_TYPE)
                         || (enc[idx] == IA5STR_TYPE) || (enc[idx] == UNIVSTR_TYPE)))) {
             idx++;
             short size = (short)(enc[idx++] & 0xff);
             if (size >= 128) {
                 short tmp = (short)(size - 128);
                 // NOTE: for now, all sizes must fit short two bytes
                 if ((tmp > 2) || ((short)(idx + tmp) > enc.length)) {
                    ISOException.throwIt(SW_LEN_1_ERROR);
                 } else {
                     size = 0;
                     while (tmp > 0) {
                         size = (short)((size << 8) + (enc[(short)(idx++)] & 0xff));
                         tmp--;
                     }
                 }
             }
             return size;
         }
         ISOException.throwIt(SW_LEN_2_ERROR);
         return (short)0;
     }

     /**
      * Expects to see a PKCS1 algorithm identifier in the DER encoding
      * (enc) starting at the current offset (idx).
      * <P />
      * @return a single-byte algorithm identifier, e.g. MD5_RSA, MD2_RSA
      * @exception IOException if an error is encountered during parsing
      */
     private byte getAlg() {
         byte val=0;

         try {
             match(PKCS1Seq);
             val = enc[idx++];
             match(NullSeq);
             return val;
         } catch (Exception e) {
            ISOException.throwIt(ISO7816.SW_DATA_INVALID);
         }
         return val;
     }

     /**
      * Parses a SubjectName or IssuerName in the DER encoding
      * (enc) starting at the current offset (idx) and ending
      * at end.
      * <P />
      * @param end ending offset for the DER-encoded name
      * @return a human friendly byte[] representation of the name
      */
     private byte[] getName(short end) {
         byte[] name = new byte[3];
         //Util.arrayCopy(enc, idx, name, (short)0, (short)(end-idx));
 
         
         for(short i = idx; i < end-3;i++){
            if(enc[i] == (byte) 68 && enc[i+1] == (byte) 79 && enc[i+2] == (byte) 77){
               Util.arrayCopy(enc,(short)(i+3), name, (short)0, (short)3);
            }
         }
         
         idx = end;
         //System.out.println(new String(name));
         
         //for(short i=0; i<end;i++) {
           // System.out.print(name[i] + " ");
         //}
         
         return name;
//         
//         short nameLen = 0;
//         short len = 0;
//         short cidx; // index where the most recently seen name component starts
//         short clen; // Component length
//         byte[] label = null;
//         short aidx;
//
//         while (idx < end) {
//             if (nameLen != 0) {
//                 // this is not the first time so insert a separator
//                 name[(short)(nameLen++)] = (byte) ';';
//             }
//
//             getLen(SET_TYPE);
//             getLen(SEQUENCE_TYPE);
//
//             /*
//              * Save the start of name component, e.g CommonName
//              * ... and its length
//              */
//             clen = getLen(OID_TYPE);
//             cidx = idx;
//             idx += clen;
//
//             /*
//              * At this point we tag the name component, e.g. C= or hex
//              * if unknown.
//              */
//             if ((clen == 3) && (enc[cidx] == 0x55)
//                     && (enc[cidx + 1] == 0x04)) {
//                 // begins with id-at, so try to see if we have a label
//                 aidx = (short)(enc[cidx + 2] & 0xFF);
//                 if ((aidx < nameAttr.length)
//                         && (nameAttr[aidx][0] != 0)) {
//                    label = new byte[nameAttr[aidx].length];
//                    Util.arrayCopy(nameAttr[aidx], (short)0, label, (short)0, (short)nameAttr[aidx].length);
//                 } else {
//                    Util.arrayCopy(enc, cidx, label, (short)0, clen);
//                 }
//             } else if (Util.arrayCompare(enc, cidx, EMAIL_ATTR_OID, (short)0,(short)
//                     EMAIL_ATTR_OID.length)==0) {
//                Util.arrayCopy(EMAIL_ATTR_LABEL, (short)0, label, (short)0, (short)EMAIL_ATTR_LABEL.length);
//             } else {
//                Util.arrayCopy(enc, cidx, label, (short)0, clen);
//             }
//
//             for (short i = 0; i < label.length; i++) {
//                 name[(short)(nameLen++)] = (byte) label[i];
//             }
//
//             name[nameLen++] = (byte) '=';
//
//             len = getLen(ANY_STRING_TYPE);
//
//             if (len > 0) {
//                 for (short i = 0; i < len; i++) {
//                     name[(short)(nameLen++)] = enc[(short)(idx++)];
//                 }
//             }
//         }
//         
//         return name;
     }

     /**
      * Gets a byte[] representation of the UTC time whose DER ecnoding
      * is contained in the specified buffer.
      * <P />
      * @param buf buffer containing the DER encoding of UTC Time
      * @param off starting offset of the encoding inside buf
      * @return a byte[] represntation of the UTC time in the form
      * yy/mm/dd hh:mm:ss
      * @exception IOException if an error is encountered during parsing
      */
     private static Date getUTCTime(byte[] buf, short off)
             throws IOException {
         short[] period = new short[6]; // year, month, day, hour, minute, second

         if (buf[(short)(off + UTC_LENGTH - 1)] != (byte) 'Z')
            ISOException.throwIt(SW_UTCTIME_1_ERROR);
         for (short i = 0; i < 6; i++) {
             period[i] = 0;
             if ((buf[(short)(2 * i + off)] < (byte) '0')
                     || (buf[(short)(2 * i + off)] > (byte) '9'))
                ISOException.throwIt(SW_UTCTIME_2_ERROR);
             period[i] = (short)(buf[(short)(2 * i + off)] - (short) '0');
             if ((buf[(short)(2 * i + off + 1)] < (byte) '0')
                     || (buf[(short)(2 * i + off + 1)] > (byte) '9'))
                ISOException.throwIt(SW_UTCTIME_3_ERROR);
             period[i] = (short)((period[i] * 10)
                     + (buf[(short)(2 * i + off + 1)] - (short) '0'));
         }

         if (period[(short)0] < 50) { // from rfc2459
             period[(short)0] += 2000;
         } else {
             period[(short)0] += 1900;
         }

         return new Date(period);
     }

     /**
      * Parses X.509v3 extensions in the certificate encoding until
      * the specified index.
      * <p />
      * @param end index of the last byte in the certificate encoding
      *        to be processed
      */
     private void parseExtensions(short end) {
         /*
          * NOTE: If one does not wish to support v3 extensions
          * at all (to save code), one can simply set badExt to
          * true and return -- the code that actually parses extensions
          * can be commented out
          */
         short extId = -1;
         short extIdIdx = 0;
         short extIdLen = 0;
         boolean crit;
         short extValIdx = 0;
         short extValLen = 0;
         short tmp;

         getLen((byte) 0xa3); // extensions start with 0xa3
         getLen(SEQUENCE_TYPE);
         while (idx < end) {
             extId = -1;
             getLen(SEQUENCE_TYPE);
             extIdLen = getLen(OID_TYPE);
             extIdIdx = idx;
             idx += extIdLen;
             crit = false;
             if ((enc[(short)idx] == 0x01) && (enc[(short)(idx + 1)] == 0x01)) {
                 idx += 2;
                 crit = (enc[idx++] == (byte) 0xff) ? true : false;
             }
             extValLen = getLen(OCTETSTR_TYPE);
             extValIdx = idx;
             if ((enc[(short)extIdIdx] == 0x55) && (enc[(short)(extIdIdx + 1)] == 0x1d)) {
                 // Do we recognize this? NOTE: id-ce is 0x55, 0x1d
                 switch (enc[(short)(extIdIdx + 2)] & 0xff) {
                 case 0x0f: // keyUsage = id-ce 15
                     extId = 15;//"KU";
                     if (keyUsage == -1) {
                         keyUsage = 0;
                     }

                     tmp = (short)(getLen(BITSTRING_TYPE) - 1);
                     short unused = enc[(short)(idx++)]; // get unused bits in last octet
                     byte b = 0;

                     // process each bit in the bitbyte[] starting with
                     // the most significant
                     for (short i = 0; i < (short)((tmp << 3) - unused); i++) {
                         if ((i % 8) == 0) {
                             b = enc[(short)(idx++)];
                         }

                         if (b < 0) {
                             keyUsage |= 1 << i;
                         }

                         b = (byte) (b << 1);
                     }

                     break;

                 case 0x11: // subAltName = id-ce 17
                     short start = (short)(idx + 4);
                     short length = (short)(extValLen - 4);
                     extId = 17;//"SAN";

                     /*
                      * First byte stores the type e.g. 1=rfc822Name(email),
                      * 2=dNSName, 6=URI etc
                      */
                     subAltNameType = (byte) (enc[(short)(idx + 2)] - 0x80);

                   
                     switch (subAltNameType) {
                     case TYPE_EMAIL_ADDRESS:
                     case TYPE_DNS_NAME:
                     case TYPE_URI:
//                         for (short i = 0; i < length; i++) {
//                             temp.append((byte) enc[start + i]);
//                         }
//
//                         subAltName = temp.tobyte[]();
                         break;

                     default:
                         subAltName = new byte[length];
                         for (short i = 0; i < length; i++) {
                             ((byte[]) subAltName)[i] = enc[(short)(start + i)];
                         }
                     }
                     break;
                 case 0x13: // basicConstr = id-ce 19
                     hasBC = true;
                     extId = 19;//"BC";
                     tmp = getLen(SEQUENCE_TYPE);
                     if (tmp == 0)
                         break;
                     // ca is encoded as an ASN boolean (default is false)
                     if ((enc[(short)idx] == 0x01) && (enc[(short)(idx + 1)] == 0x01)
                             && (enc[(short)(idx + 2)] == (byte) 0xff)) {
                         isCA = true;
                         idx += 3;
                     }

                     /*
                      * path length constraint is encoded as optional ASN
                      * integer
                      */
                     if ((enc[idx] == 0x02) && (enc[(short)(idx + 1)] != 0)) {
                         tmp = getLen(INTEGER_TYPE);
                         pLenConstr = 0;
                         for (short i = 0; i < tmp; i++) {
                             pLenConstr = (short)((pLenConstr << 16)
                                     + enc[(short)(idx + i)]);
                         }
                         idx += tmp;
                     } else {
                         if (isCA)
                             pLenConstr = UNLIMITED_CERT_CHAIN_LENGTH;
                     }
                     break;

                 case 0x25: // extendedKeyUsage = id-ce 37
                     extId = 37;// "EKU";
                     if (extKeyUsage == -1) {
                         extKeyUsage = 0;
                     }

                     getLen(SEQUENCE_TYPE);
                     short kuOidLen;
                     while (idx < (short)(extValIdx + extValLen)) {
                         kuOidLen = getLen(OID_TYPE);
                         if ((kuOidLen == (short)(ID_KP.length + 1))
                                 && Util.arrayCompare(enc, idx, ID_KP, (short)0,
                                       (short)ID_KP.length)==0
                                 && (enc[(short)(idx + ID_KP.length)] > 0)
                                 && (enc[(short)(idx + ID_KP.length)] < 9)) {
                             extKeyUsage |= (1 << (enc[(short)(idx + ID_KP.length)]));
                         } else {
                             if (crit)
                                 badExt = true;
                         }
                         idx += kuOidLen;
                     }

                     if (!crit) {
                         // ignore extended key usage if not critical
                         extKeyUsage = -1;
                     }

                     break;
                 /*
                  * Extensions which we do not currently support include:
                  * subjectDirectoryAttribute 0x09,
                  * subjectKeyIdentifier 0x0e, privateKeyUsagePeriod 0x10,
                  * issuerAltName 0x12, cRLNumber 0x14, reasonCode 0x15,
                  * instructionCode 0x17, invalidityDate 0x18,
                  * deltaCRLIndicator 0x1b, issuingDistributionPoint 0x1c,
                  * certificateIssuer 0x1d, nameConstraints 0x1e,
                  * cRLDistributionPoints 0x1f, certificatePolicies 0x20,
                  * policyMappings 0x21, authorityKeyIdentifier 0x23,
                  * policyConstraints 0x24
                  */
                 }
             }

                          if ((extId == -1) && crit)
                 badExt = true;

             idx = (short)(extValIdx + extValLen);
         }

         if (idx != end) {
            ISOException.throwIt(SW_EXTENSIONS_INVALID);
         }

     } // Done processing extensions

     /**
      * Creates a certificate by parsing the ASN.1 DER X.509 certificate
      * encoding in the specified buffer.<BR />
      * <B>NOTE:</B> In the standard edition, equivalent functionality
      * is provided by CertificateFactory.generateCertificate(InputStream).
      * <P />
      * @param buf byte array to be read
      * @param off offset within the byte array
      * @param len number of bytes to be read
      * @return a certificate object corresponding to the DER encoding
      *         or null (in case of an encoding problem)
      * @exception IOException if there is a parsing error
      */
     public void parseCertificate(byte[] buf,
             short off, short len){
         /*
          * force bad parameter errors now, so later we can consider any out of
          * bounds errors to be parsing errors
          */
         short test = (short)(buf[(short)off] + (short)buf[(short)(len - 1)] + (short)buf[(short)(off + len - 1)]);

         try {
             short start = 0;
             short size = 0;
             byte[] hash = new byte[20]; // for SHA1 fingerprint

             short publicKeyLen;
             short publicKeyPos;
             short modulusPos;
             short modulusLen;
             short exponentPos;
             short exponentLen;

             // Compute the MD5 fingerprint
             MessageDigest md = MessageDigest.getInstance(MessageDigest.ALG_SHA, false);
             
             md.doFinal(buf, (short)off, (short)len, hash, (short)0);

             /*
              * Create a new certificate and fill its attributes by parsing
              * the DER encoding
              */

             // Prepare to parse this certificate
             this.idx = 0;
             // Set the encoding
             this.enc = new byte[len];
             Util.arrayCopy(buf, (short)off, this.enc, (short)0, (short)len);
             // ... and the fingerprint
             this.fp = new byte[hash.length];
             Util.arrayCopy(hash, (short)0, this.fp, (short)0, (short)hash.length);

             /*
              * A Certificate is a sequence of a TBSCertificate, a signature
              * algorithm identifier and the signature
              */
             this.getLen(SEQUENCE_TYPE);
             // Now read the TBS certificate
             this.TBSStart = this.idx;
             size = this.getLen(SEQUENCE_TYPE);
           
             short sigAlgIdx = (short)(this.idx + size);
             this.TBSLen = (short)(sigAlgIdx - this.TBSStart);
             // Now parse the version
             if ((this.enc[this.idx] & 0xf0) == 0xa0) {
                this.idx++;
               
                 size = (short)(this.enc[this.idx++] & 0xff);
                 if ((short)(this.idx + size) > this.enc.length) {
                    ISOException.throwIt(SW_CERT_DATA_INVALID);
                 }

                 this.version = (byte) (this.enc[(short)(this.idx + (size - 1))]);
                 this.idx += size;
             } else {
                this.version = 1; // No explicit version value
             }

             // Expect the serial number coded as an integer
             size = this.getLen(INTEGER_TYPE);
             serialNumber = new byte[size];
             Util.arrayCopy(this.enc, this.idx, serialNumber, (short)0, (short)size);
             this.idx += size;

             // Expect the signature AlgorithmIdentifier
             byte id = this.getAlg();
                         
             // Expect the issuer name
             start = this.idx;
             size = this.getLen(SEQUENCE_TYPE);
             short end = (short)(this.idx + size);
             try {
                this.issuer = this.getName(end);
                 
             } catch (Exception e) {
                ISOException.throwIt(SW_CERT_ISSUER_INVALID);
             }
             
           
             // Validity is a sequence of two UTCTime values
             try {
                this.match(ValiditySeq);
                 // get start time
                this.match(UTCSeq);
                this.from = getUTCTime(this.enc, this.idx);
                this.idx += UTC_LENGTH;
                 // get end time
                this.match(UTCSeq);
                this.until = getUTCTime(this.enc, this.idx);
                this.idx += UTC_LENGTH;
             } catch (Exception e) {
                ISOException.throwIt(SW_CERT_DATA_INVALID);
             }

             // Expect the subject name
             start = this.idx;
             size = this.getLen(SEQUENCE_TYPE);
             end = (short)(this.idx + size);
           
             if (size != 0) {
                 try {
                    this.subject = this.getName(end);
                 } catch (Exception e) {
                    ISOException.throwIt(SW_CERT_SUBJECT_INVALID);
                 }
             }
             if (Util.arrayCompare(subject, (short)0, issuer, (short)0, (short)subject.length)==0) {
                 selfSigned = true;
             }
             //System.out.println(new String(subject));
             // NOTE: the subject can be null (empty sequence) if
             // subjectAltName is present

             // Parse the subject public key information
           

             publicKeyLen = this.getLen(SEQUENCE_TYPE);
             publicKeyPos = this.idx;

             // Match the algorithm Id
             id = this.getAlg();
             

             if (id != RSA_ENCRYPTION) {
                 // skip the public key
                this.idx = (short)(publicKeyPos + publicKeyLen);
             }

             // Get the bit byte[]
             this.getLen(BITSTRING_TYPE);
             if (this.enc[this.idx++] != 0x00) {
                ISOException.throwIt(SW_CERT_DATA_INVALID);
             }

             this.getLen(SEQUENCE_TYPE);
             size = this.getLen(INTEGER_TYPE);
             if (this.enc[this.idx] == (byte) 0x00) {
                 // strip off the sign byte
                 size--;
                 this.idx++;
             }

             // Build the RSAPublicKey
             modulusPos = this.idx;
             modulusLen = size;         

             this.idx += size;

             size = this.getLen(INTEGER_TYPE);
             if (this.enc[this.idx] == (byte) 0x00) {
                 // strip off the sign byte
                 size--;
                 this.idx++;
             }

             exponentPos = this.idx;
             exponentLen = size;           
             
             this.pubKey =(RSAPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PUBLIC, (short)(modulusLen*8), false);
             pubKey.setExponent(this.enc, (short)exponentPos, (short)exponentLen);
             pubKey.setModulus(this.enc, (short)modulusPos, (short)modulusLen);

             this.idx += size;
             if (this.idx != sigAlgIdx) {
                if (this.version <= 1) {
//                   ISOException.throwIt(SW_CERT_DATA_INVALID);
                 } else {
                    this.parseExtensions(sigAlgIdx);
                 }
             }

             // get the signatureAlgorithm
             this.sigAlg = this.getAlg();

             
             /*
              * If this is a supported signature algorithm, compute and save
              * the hash of TBSCertificate. A null TBSCertHash indicates
              * the use of an unsupported signature algorithm (see verify())
              */
             md = null;
             if (this.sigAlg == SHA1_RSA) {
                 md = MessageDigest.getInstance(MessageDigest.ALG_SHA, false);
             }

             if (md != null) {
                this.TBSCertHash = new byte[md.getLength()];
                 md.doFinal(buf, (short)(off + this.TBSStart), (short)this.TBSLen, this.TBSCertHash, (short)0);
             }

             // get the signature
             size = this.getLen(BITSTRING_TYPE);
             if (this.enc[this.idx++] != 0x00) {
                ISOException.throwIt(SW_CERT_SIG_INVALID);
             }

             /*
              * We pad the signature to a multiple of 8-bytes before storing
              * since we only support RSA modulus lengths that are multiples
              * of 8 bytes and the two should match for decryption to succeed.
              */
             short sigLen = (short)(((short)((size - 1) + 7) >>> 3) << 3);
             this.signature = new byte[sigLen];
             Util.arrayCopy(this.enc, (short)this.idx, this.signature,
                     (short)(sigLen - (size - 1)), (short)(size - 1));

           
             return;
         } catch (Exception e) {
            ISOException.throwIt(SW_CERT_PARSE_FAILED);
         }
     }



     /**
      * Gets the MD5 fingerprint of this certificate.<BR />
      * <b>NOTE:</b> this implementation returns a byte array filled
      * with zeros if there is no fingerprint associated with this
      * certificate. This may happen if a null was passed to the
      * X509Certificate constructor.
      * <P />
      * @return a byte array containing this certificate's MD5 hash
      */
     public byte[] getFingerprint() {
         byte[] res = new byte[16];
         if (fp != null)
            Util.arrayCopy(fp, (short)0, res, (short)0, (short)res.length);
         return res;
     }

     /**
      * Gets the name of this certificate's issuer. <BR />
      * <B>NOTE:</B> FORMAT is: C=BE;OU=eGOV;CN=NAME
      * <P />
      * @return a byte[] containing this certificate's issuer in
      * user-friendly form
      */
     public byte[] getIssuer() {
         return issuer;
     }

     /**
      * Gets the name of this certificate's subject. <BR />
      * <B>NOTE:</B> FORMAT is: C=BE;OU=eGOV;CN=NAME
      * <P />
      * @return a byte[] containing this certificate's subject in
      * user-friendly form
      */
     public byte[] getSubject() {
         return subject;
     }

     /**
      * Gets the NotBefore date from the certificate's validity period.
      * <P />
      * @return a date before which the certificate is not valid
      */
     public Date getNotBefore() {
         return from;
     }

     /**
      * Gets the NotAfter date from the certificate's validity period.
      *
      * @return a date after which the certificate is not valid (expiration
      * date)                                           
      */
     public Date getNotAfter() {
         return until;
     }

     /**
      * Checks if a certificate has any (version 3) extensions that
      * were not properly processed and continued use of this certificate
      * may be inconsistent with the issuer's intent. This may happen, for
      * example, if the certificate has unrecognized critical extensions.
      *
      * @exception CertificateException with a reason ofr BAD_EXTENSIONS if
      *    there are any bad extensions
      */
     public void checkExtensions() {
         if (badExt) {
            ISOException.throwIt(ISO7816.SW_WRONG_DATA);
         }
     }

     /**
      * Checks if the certificate is valid on the specified time. It is
      * if the specified time is within the certificate's validity
      * period.
      * @param time the time in milliseconds for which a certificate's
      * validity is to be checked
      */
     public short checkValidity(Date time) {
       
         if (time.isDateOlder(from)==-1) { //time < from
            return (short)1; //not yet valid
         }

         if (time.isDateOlder(until)==1) { //time > until
             return (short)-1; // expired
         }
         return (short)0; //valid
     }
   

     /**
      * Gets the public key from this certificate.
      * <P />
      * @return the public key contained in the certificate
      *
      * @exception CertificateException if public key is not a supported type
      *            (could not be parsed).
      */
     public PublicKey getPublicKey()  {
         if (pubKey == null) {
            ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED);
         }
         return pubKey;
     }

     /**
      * Gets the raw X.509 version number of this certificate. Version 1 is 0.
      *
      * @return the X.509 logic version number (1, 2, 3) of the certificate
      */
     public short getVersion() {
         return (short)(version+1);
     }

     /**
      * Gets the certificate constraints path length from the
      * <code>BasicConstraints</code> extension. <P />
      *
      * The <code>BasicConstraints</code> extension identifies whether the
      * subject of the certificate is a Certificate Authority (CA) and how
      * deep a certification path may exist through the CA. The
      * <code>pathLenConstraint</code> field (see below) is meaningful only
      * if <code>cA</code> is set to TRUE. In this case, it gives the maximum
      * number of CA certificates that may follow this certificate in a
      * certification path. A value of zero indicates that only an end-entity
      * certificate may follow in the path. <P />
      *
      * Note that for RFC 2459 this extension is always marked critical
      * if <code>cA</code> is TRUE, meaning this certificate belongs to a
      * Certificate Authority. <P />
      *
      * The ASN.1 definition for this is:
      * <PRE>
      *  BasicConstraints ::= SEQUENCE {
      *        cA                  BOOLEAN DEFAULT FALSE,
      *        pathLenConstraint   INTEGER (0..MAX) OPTIONAL
      *  }
      *  </PRE>
      *
      * @return MISSING_PATH_LENGTH_CONSTRAINT if the
      * <code>BasicConstraints</code> extension is absent or the subject
      * of the certificate is not a CA. If the subject of the certificate
      * is a CA and <code>pathLenConstraint</code> does not appear,
      * <code>UNLIMITED_CERT_CHAIN_LENGTH</code> is returned to indicate that
      * there is no limit to the allowed length of the certification path.
      * In all other situations, the actual value of the
      * <code>pathLenConstraint</code> is returned.
      */
     public short getBasicConstraints() {
         if (isCA) {
             return pLenConstr;
         } else {
             return MISSING_PATH_LENGTH_CONSTRAINT;
         }
     }

     /**
      * Gets a bit vector (in the form of an short) in which
      * each position represents a purpose for which the public key in
      * the certificate may be used (iff that bit is set). The correspondence
      * between bit positions and purposes is as follows: <BR />
      * <TABLE>
      * <TR><TD>digitalSignature</TD> <TD>0</TD> </TR>
      * <TR><TD>nonRepudiation</TD>   <TD>1</TD> </TR>
      * <TR><TD>keyEncipherment</TD>  <TD>2</TD> </TR>
      * <TR><TD>dataEncipherment</TD> <TD>3</TD> </TR>
      * <TR><TD>keyAgreement</TD>     <TD>4</TD> </TR>
      * <TR><TD>keyCertSign</TD>      <TD>5</TD> </TR>
      * <TR><TD>cRLSign</TD>          <TD>6</TD> </TR>
      * <TR><TD>encipherOnly</TD>     <TD>7</TD> </TR>
      * <TR><TD>decipherOnly</TD>     <TD>8</TD> </TR>
      * </TABLE>
      * <P />
      * @return a bitvector indicating approved key usage of the certificate
      * public key, -1 if a KeyUsage extension is not present.
      */
     public short getKeyUsage() {
         return keyUsage;
     }

     /**
      * Gets a bit vector (in the form of an short) in which
      * each position represents a purpose for which the public key in
      * the certificate may be used (iff that bit is set). The correspondence
      * between bit positions and purposes is as follows: <BR />
      * <TABLE>
      * <TR><TD>serverAuth</TD>       <TD>1</TD> </TR>
      * <TR><TD>clientAuth</TD>       <TD>2</TD> </TR>
      * <TR><TD>codeSigning</TD>      <TD>3</TD> </TR>
      * <TR><TD>emailProtection</TD>  <TD>4</TD> </TR>
      * <TR><TD>ipsecEndSystem</TD>   <TD>5</TD> </TR>
      * <TR><TD>ipsecTunnel</TD>      <TD>6</TD> </TR>
      * <TR><TD>ipsecUser</TD>        <TD>7</TD> </TR>
      * <TR><TD>timeStamping</TD>     <TD>8</TD> </TR>
      * </TABLE>
      * <P />
      * @return a bitvector indicating extended usage of the certificate
      * public key, -1 if a critical extendedKeyUsage extension is not present.
      */
     public short getExtKeyUsage() {
         return extKeyUsage;
     }

     /**
      * Gets the type of subject alternative name.
      *
      * @return type of subject alternative name
      */
     public short getSubjectAltNameType() {
         return subAltNameType;
     }

     /**
      * Gets the subject alternative name or null if it was not in the
      * certificate.
      *
      * @return type of subject alternative name or null
      */
     public Object getSubjectAltName() {
         return subAltName;
     }

     /**
      * Gets the printable form of the serial number of this
      * <CODE>Certificate</CODE>.
      * If the serial number within the <CODE>certificate</CODE>
      * is binary is should be formatted as a byte[] using
      * hexadecimal notation with each byte represented as two
      * hex digits separated byte ":" (Unicode x3A).
      * For example,  27:56:FA:80.
      * @return A byte[] containing the serial number
      * in user-friendly form; <CODE>NULL</CODE> is returned
      * if there is no serial number.
      */
     public byte[] getSerialNumber() {
         return serialNumber;
     }

     /**
      * Checks if this certificate was signed using the private key
      * corresponding to the specified public key.
      *
      * @param k public key to be used for verifying certificate signature
      *
      * @exception CertificateException if there is an error
      */
     public void verify(PublicKey k) {
         RSAPublicKey pk;

         if (!(k instanceof  PublicKey)) {
            ISOException.throwIt(ISO7816.SW_DATA_INVALID);
         }

         pk = (RSAPublicKey) k;

         /*
          * NOTE: uncomment if selfSigned certificates must not be checked
          *
         if (selfSigned) {
             if (pubKey.equals(pk)) {
                 return;
             }

             ISOException.throwIt(ISO7816.SW_DATA_INVALID);
             return;
         }
          */
         if (signature == null) {
            ISOException.throwIt(ISO7816.SW_DATA_INVALID);
            return;
         }

         if (TBSCertHash == null) {
            ISOException.throwIt(ISO7816.SW_DATA_INVALID);
            return;
         }

         short modLen = pk.getSize();
         byte[] result = new byte[(short)(modLen/8)];

         short val;

         /*
          * NOTE: We can not use the Signature class because, at this
          * point, we do not have TBSCertificate (just its hash). The
          * Signature class needs raw data and computes a hash internally.
          */
         try {
             Cipher rsa = Cipher.getInstance(Cipher.ALG_RSA_PKCS1, false);

             rsa.init(pk, Cipher.MODE_DECRYPT);
             val = rsa.doFinal(signature, (short)0, (short)signature.length, result, (short)0);
         } catch (Exception e) {
            ISOException.throwIt(ISO7816.SW_DATA_INVALID);
            return ;
         }

         /*
          * NOTE: the decrypted value includes an ASN DER
          * encoding of
          * DigestInfo ::= SEQUENCE {
          *       digestAlgorithm DigestAlgorithmIdentifier,
          *       digest Digest }
          * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
          * Digest ::= OCTET byte[]
          *
          *
          * For SHA-1, the 20-byte hash will be preceded by
          * 3021300906052b0e03021a05000414
          * 30 21       33: SEQUENCE
          * 30 09        9: . SEQUENCE
          * 06 05        5: . . OID 1.3.14.3.2.26 (SHA-1 digest OID)
          *              0: 2b 0e 03 02 1a
          * 05 00        0: . . NULL (null parameters)
          * 04 14       20: . <20-byte hash>
          */

         if ((sigAlg == SHA1_RSA)
                 && (val == (short)(PREFIX_SHA1.length + TBSCertHash.length))
                 && Util.arrayCompare(result, (short)0, PREFIX_SHA1, (short)0,
                       (short)PREFIX_SHA1.length)==0
                 && Util.arrayCompare(result, (short)PREFIX_SHA1.length,
                         TBSCertHash, (short)0, (short)TBSCertHash.length)==0) {
             return;
         }

         ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
     }

     /**
      * Gets the name of the algorithm used to sign the certificate.
      * <P />
      * @return the name of signature algorithm
      */
     public byte[] getSigAlgName() {
         /*
          * These are ordered to maximize the likelihood of an
          * early match, md5WithRSA seems the most common
          */
        if (sigAlg == SHA1_RSA)
           return new byte[]{'S','H','A','1','w','i','t','h','R','S','A'};
         else if (sigAlg == NONE)
             return new byte[]{'N','o','n','e'};
        else
           return new byte[]{'U','n','k','n','o','w','n','(',sigAlg,')'};
     }

   
 }
The greatest glory in living lies not in never falling, but in rising every time we fall.--Nelson Mandela

Liuyaxue
Posts: 3
Joined: Wed Oct 17, 2018 7:35 am
Points :26
Contact:

Re: parsing X509 certificate

Post by Liuyaxue » Wed Oct 17, 2018 8:03 am

hi ,Excuse my poor English,Here are the code which implements methods for creating X.509 certificates , the algorithm hope to use is not RSA, can be AES, DES, or ECC and so on.Hope this helps!

Liuyaxue
Posts: 3
Joined: Wed Oct 17, 2018 7:35 am
Points :26
Contact:

Re: parsing X509 certificate

Post by Liuyaxue » Wed Oct 17, 2018 8:08 am

hi ,Excuse my poor English,Here are the code which implements methods for creating X.509 certificates , the algorithm hope to use is not RSA, can be AES, DES, or ECC and so on.Hope this helps!

brownell
Posts: 1
Joined: Sat Oct 20, 2018 12:27 pm
Points :8
Contact:

Re: parsing X509 certificate

Post by brownell » Sat Oct 20, 2018 12:32 pm

This actually helped!! Thanks for providing the code to create the X.509 certificates.

deepaklucky
Posts: 4
Joined: Tue May 22, 2018 1:43 pm
Points :208
Contact:

Re: parsing X509 certificate

Post by deepaklucky » Tue Nov 19, 2019 4:32 am

Hello,
Thanks for the code, can u please let me know what is the import used for Date class used in provided source code ? it will be very help full.

Post Reply Previous topicNext topic

Who is online

Users browsing this forum: No registered users and 40 guests

JavaCard OS : Disclaimer