Index: modules/security/src/main/java/common/org/apache/harmony/security/provider/cert/X509CertPathImpl.java =================================================================== --- modules/security/src/main/java/common/org/apache/harmony/security/provider/cert/X509CertPathImpl.java (revision 410209) +++ modules/security/src/main/java/common/org/apache/harmony/security/provider/cert/X509CertPathImpl.java (working copy) @@ -15,9 +15,9 @@ */ /** -* @author Alexander Y. Kleymenov -* @version $Revision$ -*/ + * @author Alexander Y. Kleymenov + * @version $Revision$ + */ package org.apache.harmony.security.provider.cert; @@ -41,12 +41,33 @@ import org.apache.harmony.security.pkcs7.SignedData; import org.apache.harmony.security.x509.Certificate; - /** - * X509CertPathImpl + * This class is an implementation of X.509 CertPath. This implementation + * provides ability to create the instance of X.509 Certification Path + * by several means:
+ * + *   1. It can be created over the list of X.509 certificates + * (implementations of X509Certificate class) provided in constructor.
+ * + *   2. It can be created by means of getInstance methods + * on the base of the following ASN.1 DER encoded forms:
+ * + *    - PkiPath as defined in + * ITU-T Recommendation X.509(2000) Corrigendum 1(2001) + * (can be seen at + * ftp://ftp.bull.com/pub/OSIdirectory/DefectResolution/TechnicalCorrigenda/ApprovedTechnicalCorrigendaToX.509/8%7CX.509-TC1(4th).pdf) + *
+ *    - PKCS #7 SignedData object provided in the form of + * ContentInfo structure. CertPath object is generated on the base of + * certificates presented in ‘certificates’ field of the SignedData + * object which in its turn is retrieved from ContentInfo structure. + * (see http://www.rsasecurity.com/rsalabs/node.asp?id=2129 + * for more info on PKCS #7) + *
+ *   */ public class X509CertPathImpl extends CertPath { - + /** * @serial */ @@ -55,29 +76,32 @@ // supported encoding types: public static final int PKI_PATH = 0; public static final int PKCS7 = 1; - - // supported encoding names: - private static final String[] encodingsArr = + + // supported encoding names + private static final String[] encodingsArr = new String[] {"PkiPath", "PKCS7"}; static final List encodings = Collections.unmodifiableList( Arrays.asList(encodingsArr)); + // the list of certificates representing this certification path private final List certificates; + // PkiPath encoding of the certification path private byte[] pkiPathEncoding; + // PKCS7 encoding of the certification path private byte[] pkcs7Encoding; - + + /** + * Creates an instance of X.509 Certification Path over the specified + * list of certificates. + * @throws CertificateException if some of the object in the list + * is not an instance of subclass of X509Certificate. + */ public X509CertPathImpl(List certs) throws CertificateException { super("X.509"); - // if (certs == null) { - // throw new CertificateException( - // "Provided list of certificates provided is null."); - // } - // throw NullPointerException: int size = certs.size(); certificates = new ArrayList(size); for (int i=0; iencoding. + * @throws CertificateException if specified encoding form is not supported, + * or some problems occurred during the decoding. + */ + public static X509CertPathImpl getInstance(InputStream in, String encoding) throws CertificateException { if (!encodings.contains(encoding)) { throw new CertificateException( @@ -115,16 +161,19 @@ } try { if (encodingsArr[0].equals(encoding)) { + // generate the object from PkiPath encoded form return (X509CertPathImpl) ASN1.decode(in); } else { + // generate the object from PKCS #7 encoded form ContentInfo ci = (ContentInfo) ContentInfo.ASN1.decode(in); SignedData sd = ci.getSignedData(); if (sd == null) { throw new CertificateException( - "Incorrect PKCS7 encoded form: missing signed data"); + "Incorrect PKCS7 encoded form: missing signed data"); } List certs = sd.getCertificates(); if (certs == null) { + // empty chain of certificates certs = new ArrayList(); } return new X509CertPathImpl(certs, PKCS7, ci.getEncoded()); @@ -135,7 +184,13 @@ } } - public static X509CertPathImpl getInstance(byte[] in) + /** + * Generates certification path object on the base of PkiPath + * encoded form provided via array of bytes. + * @throws CertificateException if some problems occurred during + * the decoding. + */ + public static X509CertPathImpl getInstance(byte[] in) throws CertificateException { try { return (X509CertPathImpl) ASN1.decode(in); @@ -145,7 +200,14 @@ } } - public static X509CertPathImpl getInstance(byte[] in, String encoding) + /** + * Generates certification path object on the base of encoding provided via + * array of bytes. The format of provided encoded form is specified by + * parameter encoding. + * @throws CertificateException if specified encoding form is not supported, + * or some problems occurred during the decoding. + */ + public static X509CertPathImpl getInstance(byte[] in, String encoding) throws CertificateException { if (!encodings.contains(encoding)) { throw new CertificateException( @@ -153,13 +215,15 @@ } try { if (encodingsArr[0].equals(encoding)) { + // generate the object from PkiPath encoded form return (X509CertPathImpl) ASN1.decode(in); - } else { // PKCS7 + } else { + // generate the object from PKCS #7 encoded form ContentInfo ci = (ContentInfo) ContentInfo.ASN1.decode(in); SignedData sd = ci.getSignedData(); if (sd == null) { throw new CertificateException( - "Incorrect PKCS7 encoded form: missing signed data"); + "Incorrect PKCS7 encoded form: missing signed data"); } List certs = sd.getCertificates(); if (certs == null) { @@ -173,15 +237,21 @@ } } + // --------------------------------------------------------------------- + // ---- java.security.cert.CertPath abstract method implementations ---- + // --------------------------------------------------------------------- + /** - * getCertificates + * @see java.security.cert.CertPath#getCertificates() + * method documentation for more info */ public List getCertificates() { return Collections.unmodifiableList(certificates); } /** - * getEncoded + * @see java.security.cert.CertPath#getEncoded() + * method documentation for more info */ public byte[] getEncoded() throws CertificateEncodingException { if (pkiPathEncoding == null) { @@ -193,7 +263,8 @@ } /** - * getEncoded + * @see java.security.cert.CertPath#getEncoded(String) + * method documentation for more info */ public byte[] getEncoded(String encoding) throws CertificateEncodingException { @@ -202,47 +273,70 @@ "Unsupported encoding: "+encoding); } if (encodingsArr[0].equals(encoding)) { + // PkiPath encoded form return getEncoded(); } else { - // FIXME: PKCS7 encoding support - // PKCS7 encoded form: + // PKCS7 encoded form if (pkcs7Encoding == null) { - SignedData sd = new SignedData(1, new ArrayList(), + SignedData sd = new SignedData(1, new ArrayList(), new ContentInfo(ContentInfo.DATA, null), certificates, null, new ArrayList()); ContentInfo ci = new ContentInfo(ContentInfo.SIGNED_DATA, sd); pkcs7Encoding = ci.getEncoded(); } byte[] result = new byte[pkiPathEncoding.length]; - System.arraycopy(pkcs7Encoding, 0, result, 0, + System.arraycopy(pkcs7Encoding, 0, result, 0, pkcs7Encoding.length); return result; } } /** - * getEncodings + * @see java.security.cert.CertPath#getEncodings() + * method documentation for more info */ public Iterator getEncodings() { return encodings.iterator(); } - public static ASN1SequenceOf ASN1 = new ASN1SequenceOf(ASN1Any.getInstance()) { - + /** + * ASN.1 DER Encoder/Decoder for PkiPath structure. + */ + public static ASN1SequenceOf ASN1 = + new ASN1SequenceOf(ASN1Any.getInstance()) { + + /** + * Builds the instance of X509CertPathImpl on the base of the list + * of ASN.1 encodings of X.509 certificates provided via + * PkiPath structure. + * This method participates in decoding process. + */ public Object getDecodedObject(BerInputStream in) throws IOException { + // retrieve the decoded content List encodings = (List) in.content; int size = encodings.size(); List certificates = new ArrayList(size); for (int i=0; i + * + * The process of Certificate/CRL generation + * (implemented in X509CertFactoryImpl) is accompanied with + * prereading of the beginning of encoded form. This prefix is used to determine + * whether provided form is PEM encoding or not.
+ * + * So the use of the prefix is the first point to (approximately) + * determine whether object to be generated is in the cache or not. + * + * The failure of the predetermination process tells us that there were not + * object generated from the encoded form with such prefix and we should + * generate (decode) the object. If predetermination is successful, + * we conduct the accurate search on the base of whole encoded form.
+ * + * So to speed up the object generation process this caching mechanism provides + * the following functionality:
+ * + * 1. With having of the beginning of the encoded form (prefix) + * it is possible to predetermine whether object has already been + * generated on the base of the encoding with the SIMILAR prefix or not. + * This process is not computationally expensive and takes a little time. + * But it prevents us from use of expensive full encoding + * search in the case of its failure.
+ * + * 2. If predetermination ends with success, the whole encoding + * form should be provided to make the final answer: whether object has + * already been generated on the base of this PARTICULAR encoded form or not. + * If it is so - the cached object is returned from the cache, + * if not - new object should be generated and saved in the cache.
+ * + * Note: The length of the prefixes of the encoded forms should not be + * less than correspondance (default value is 28). */ public class Cache { - + + // Hash code consist of 6 bytes: AABB00 + // where: + // AA - 2 bytes for prefix hash + // value generated on the base of the prefix of encoding + // BB - 2 bytes for tail hash + // value generated on the base of the tail of encoding + // 00 - 2 reserved bytes equals to 0 + // + // Note, that it is possible for 2 different arrays to have + // the similar hash codes. + + // The masks to work with hash codes: + // the hash code without the reserved bytes + private static final long HASH_MASK = 0xFFFFFFFFFFFF0000L; + // the hash code of the prefix + private static final long PREFIX_HASH_MASK = 0xFFFFFFFF00000000L; + // the index value contained in reserved bytes + private static final int INDEX_MASK = 0x00FFFF; + // size of the cache private final int cache_size; + // the number of bytes which will be used for array hash generation. + private final int prefix_size; + + // The following 3 arrays contain the information about cached objects. + // This information includes: hash of the array, encoded form of the object, + // and the object itself. + // The hash-encoding-object correspondence is made by means of index + // in the particular array. I.e. for index N hash contained in hashes[N] + // corresponds to the encoding contained in encodings[N] which corresponds + // to the object cached at cache[N] + // array containing the hash codes of encodings - // hash has the following structure: - // it consist of 6 bytes: - // 2 bytes for prefix hash - // 2 bytes for tail hash - // 2 reserved bytes (equals 0 in this array) private final long[] hashes; - // array containing hash<->index correspondings: - // reserved 2 bytes contains the value of the index in cache table - private final long[] hashes_idx; // array containing the encodings of the cached objects private final byte[][] encodings; - // array containing the cached certificates + // array containing the cached objects private final Object[] cache; - // how many times cached value had been demanded - //private long[] demanded = new long[cache_size]; - // the number of bytes which will be used for array hash generation. - private int prefix_size; - + // This array is used to speed up the process of the search in the cache. + // This is an ordered array of the hash codes from 'hashes' array (described + // above) with last 2 (reserved) bytes equals to the index of + // the hash in the 'hashes' array. I.e. hash code ABCD00 with index 10 in + // the hashes array will be represented in this array as ABCD0A (10==0x0A) + // So this array contains ordered correspondences. + // Note, that every item in this array is unique. + private final long[] hashes_idx; + + // the index of the last cached object private int last_cached = 0; + // cache population indicator private boolean cache_is_full = false; - private static final int INDEX_MASK = 0x00FFFF; - private static final long HASH_MASK = 0xFFFFFFFFFFFF0000L; - private static final long PREFIX_HASH_MASK = 0xFFFFFFFF00000000L; - /** - * Creates the Cache object. Capasity of the cache is size, - * - * @param size: capacity of the cache. - * @param pref_size: the number of bytes which will be used - * for array hash generation. + * Creates the Cache object. + * @param pref_size specifyes how many leading/trailing bytes of object's + * encoded form will be used for hash computation + * @param size capacity of the cache to be created. */ public Cache(int pref_size, int size) { cache_size = size; @@ -74,47 +137,113 @@ cache = new Object[cache_size]; } + /** + * Creates the Cache object of size of 900. + * @param pref_size specifyes how many leading/trailing bytes of object's + * encoded form will be used for hash computation + */ public Cache(int pref_size) { this(pref_size, 900); } - + + /** + * Creates the Cache object of size of 900. + */ public Cache() { this(28, 900); } - // Returns the hash value of the array (which length should be - // greater of equal to prefix_size). + /** + * Returns the hash code for the array. This code is used to + * predetermine whether the object was built on the base of the + * similar encoding or not (by means of contains(long) method), + * to exactly determine whether object is contained in the cache or not, + * and to put the object in the cache. + * Note: parameter array should be of length not less than + * specified by prefix_size (default 28) + * @param arr the byte array containing at least prefix_size leading bytes + * of the encoding. + * @return hash code for specified encoding prefix + */ public long getHash(byte[] arr) { long hash = 0; for (int i=1; iarr.length - prefix_size; i--) { - hash_addon += (arr[i] & 0xFF); + + /** + * Checks if there are any object in the cache generated + * on the base of encoding with prefix corresponding + * to the specified hash code. + * @param prefix_hash the hash code for the prefix + * of the encoding (retrieved by method getHash(byte[])) + * @return false if there were not any object generated + * on the base of encoding with specified hash code, true + * otherwise. + */ + public boolean contains(long prefix_hash) { + int idx = -1*Arrays.binarySearch(hashes_idx, prefix_hash)-1; + if (idx == cache_size) { + return false; + } else { + return (hashes_idx[idx] & PREFIX_HASH_MASK) == prefix_hash; } - return hash_addon << 16; } - - public void put(long hash, byte[] encoding, Object cert) { - // index pointing to the item of the table to be overwritten - int index; - // TODO: make throw out line: + + /** + * Returns the object built on the base on the specified encoded + * form if it is contained in the cache and null otherwise. + * This method is computationally expensive and should be called only if + * the method contains(long) for the hash code returned true. + * @param hash the hash code for the prefix of the encoding + * (retrieved by method getHash(byte[])) + * @param encoding encoded form of the required object. + * @return the object corresponding to specified encoding or null if + * there is no such correspondence. + */ + public Object get(long hash, byte[] encoding) { + hash |= getSuffHash(encoding); + int idx = -1*Arrays.binarySearch(hashes_idx, hash)-1; + if (idx == cache_size) { + return null; + } + while ((hashes_idx[idx] & HASH_MASK) == hash) { + int i = (int) (hashes_idx[idx] & INDEX_MASK) - 1; + if (Arrays.equals(encoding, encodings[i])) { + return cache[i]; + } + idx++; + if (idx == cache_size) { + return null; + } + } + return null; + } + + /** + * Puts the object into the cache. + * @param hash hash code for the prefix of the encoding + * @param encoding the encoded form of the object + * @param object the object to be saved in the cache + */ + public void put(long hash, byte[] encoding, Object object) { + // check for empty space in the cache if (last_cached == cache_size) { + // so cache is full, will erase the first entry in the + // cache (oldest entry). it could be better to throw out + // rarely used value instead of oldest one.. last_cached = 0; cache_is_full = true; } - index = last_cached++; + // index pointing to the item of the table to be overwritten + int index = last_cached++; - // improove the hash value (now we know the tail of encoding): + // improve the hash value with info from the tail of encoding hash |= getSuffHash(encoding); if (cache_is_full) { @@ -136,16 +265,16 @@ // hash and the same index in hash table System.out.println("WARNING: "); System.out.println(">> idx: "+idx+" new_idx: "+new_idx); - } + } } else { new_idx = -(new_idx + 1); // replace in sorted array if (new_idx > idx) { - System.arraycopy(hashes_idx, idx+1, hashes_idx, idx, + System.arraycopy(hashes_idx, idx+1, hashes_idx, idx, new_idx - idx - 1); hashes_idx[new_idx-1] = new_hash_idx; } else if (idx > new_idx) { - System.arraycopy(hashes_idx, new_idx, hashes_idx, new_idx+1, + System.arraycopy(hashes_idx, new_idx, hashes_idx, new_idx+1, idx - new_idx); hashes_idx[new_idx] = new_hash_idx; } else { // idx == new_idx @@ -160,10 +289,10 @@ idx = -(idx + 1); } idx = idx - 1; - if (idx != cache_size - index - 1) { - // if not in cell containing 0, do copy: - System.arraycopy(hashes_idx, cache_size - index, - hashes_idx, cache_size - index - 1, + if (idx != cache_size - index - 1) { + // if not in the cell containing 0 (free cell), do copy: + System.arraycopy(hashes_idx, cache_size - index, + hashes_idx, cache_size - index - 1, idx - (cache_size - index) + 1); } hashes_idx[idx] = idx_hash; @@ -171,58 +300,19 @@ // overwrite the values in the tables: hashes[index] = hash; encodings[index] = encoding; - cache[index] = cert; + cache[index] = object; } - - private boolean arrEquals(byte[] arr1, byte[] arr2) { - return Arrays.equals(arr1, arr2); - /* - // comparison from the tail: - int length = arr1.length; - if (length != arr2.length) { - return false; + + // Returns the hash code built on the base of the tail of the encoded form + // @param arr - the array containing at least prefix_size trailing bytes + // of encoded form + private long getSuffHash(byte[] arr) { + long hash_addon = 0; + for (int i=arr.length-1; i>arr.length - prefix_size; i--) { + hash_addon += (arr[i] & 0xFF); } - while (length > 0) { - if (arr1[--length] != arr2[length]) { - return false; - } - } - return true; - //*/ + return hash_addon << 16; } - public Object get(long hash, byte[] encoding) { - hash |= getSuffHash(encoding); - int idx = -1*Arrays.binarySearch(hashes_idx, hash)-1; - if (idx == cache_size) { - return null; - } - while ((hashes_idx[idx] & HASH_MASK) == hash) { - int i = (int) (hashes_idx[idx] & INDEX_MASK) - 1; - if (arrEquals(encoding, encodings[i])) { - // Uncomment to see the number of objects - // retrieved from the cache: - // - // if (XXX%2500 == 0) - // System.out.println(">> "+XXX); - // XXX++; - return cache[i]; - } - idx++; - if (idx == cache_size) { - return null; - } - } - return null; - } - - public boolean contains(long prefix_hash) { - int idx = -1*Arrays.binarySearch(hashes_idx, prefix_hash)-1; - if (idx == cache_size) { - return false; - } else { - return (hashes_idx[idx] & PREFIX_HASH_MASK) == prefix_hash; - } - } } Index: modules/security/src/main/java/common/org/apache/harmony/security/provider/cert/X509CertImpl.java =================================================================== --- modules/security/src/main/java/common/org/apache/harmony/security/provider/cert/X509CertImpl.java (revision 410209) +++ modules/security/src/main/java/common/org/apache/harmony/security/provider/cert/X509CertImpl.java (working copy) @@ -15,9 +15,9 @@ */ /** -* @author Alexander Y. Kleymenov -* @version $Revision$ -*/ + * @author Alexander Y. Kleymenov + * @version $Revision$ + */ package org.apache.harmony.security.provider.cert; @@ -51,20 +51,28 @@ import org.apache.harmony.security.x509.TBSCertificate; /** - * X509CertImpl + * This class is an implementation of X509Certificate. It wraps + * the instance of org.apache.harmony.security.x509.Certificate + * built on the base of provided ASN.1 DER encoded form of + * Certificate structure (as specified in RFC 3280 + * http://www.ietf.org/rfc/rfc3280.txt). + * @see org.apache.harmony.security.x509.Certificate + * @see java.security.cert.X509Certificate */ public class X509CertImpl extends X509Certificate { - + /** * @serial */ private static final long serialVersionUID = 2972248729446736154L; + // the core object to be wrapped in X509Certificate private final Certificate certificate; + + // to speed up access to the info, the following fields + // cache values retrieved from the certificate object private final TBSCertificate tbsCert; private final Extensions extensions; - - // cached values private long notBefore = -1; private long notAfter; private BigInteger serialNumber; @@ -75,42 +83,68 @@ private String sigAlgName; private String sigAlgOID; private byte[] sigAlgParams; + // indicates whether the signature algorithm parameters are null private boolean nullSigAlgParams; - //Values are taken directly from tbsCerttificate: - // private boolean[] issuerUniqueID; - // private boolean[] subjectUniqueID; private PublicKey publicKey; - + + // encoding of the certificate private byte[] encoding; - + + // + // ---------------------- Constructors ------------------------------- + // + + /** + * Constructs the instance on the base of ASN.1 encoded + * form of X.509 certificate provided via stream parameter. + * @param in input stream containing ASN.1 encoded form of certificate. + * @throws CertificateException if some decoding problems occur. + */ public X509CertImpl(InputStream in) throws CertificateException { try { + // decode the Certificate object this.certificate = (Certificate) Certificate.ASN1.decode(in); + // cache the values of TBSCertificate and Extensions this.tbsCert = certificate.getTbsCertificate(); this.extensions = tbsCert.getExtensions(); } catch (IOException e) { throw new CertificateException(e); } } - + + /** + * Constructs the instance on the base of existing Certificate object to + * be wrapped. + */ public X509CertImpl(Certificate certificate) { this.certificate = certificate; + // cache the values of TBSCertificate and Extensions this.tbsCert = certificate.getTbsCertificate(); this.extensions = tbsCert.getExtensions(); } + /** + * Constructs the instance on the base of ASN.1 encoded + * form of X.509 certificate provided via array of bytes. + * @param encoding byte array containing ASN.1 encoded form of certificate. + * @throws IOException if some decoding problems occur. + */ public X509CertImpl(byte[] encoding) throws IOException { - this((Certificate) Certificate.ASN1.decode(encoding)); + this((Certificate) Certificate.ASN1.decode(encoding)); } - - // + // // ----------------- Public methods implementations ------------------ // + /** + * @see java.security.cert.X509Certificate#checkValidity() + * method documentation for more information. + */ public void checkValidity() throws CertificateExpiredException, CertificateNotYetValidException { if (notBefore == -1) { + // retrieve and cache the value of validity period notBefore = tbsCert.getValidity().getNotBefore().getTime(); notAfter = tbsCert.getValidity().getNotAfter().getTime(); } @@ -123,10 +157,15 @@ } } - public void checkValidity(Date date) - throws CertificateExpiredException, + /** + * @see java.security.cert.X509Certificate#checkValidity(Date) + * method documentation for more information. + */ + public void checkValidity(Date date) + throws CertificateExpiredException, CertificateNotYetValidException { if (notBefore == -1) { + // retrieve and cache the value of validity period notBefore = tbsCert.getValidity().getNotBefore().getTime(); notAfter = tbsCert.getValidity().getNotAfter().getTime(); } @@ -138,11 +177,19 @@ throw new CertificateExpiredException(); } } - + + /** + * @see java.security.cert.X509Certificate#getVersion() + * method documentation for more information. + */ public int getVersion() { return tbsCert.getVersion() + 1; } + /** + * @see java.security.cert.X509Certificate#getSerialNumber() + * method documentation for more information. + */ public BigInteger getSerialNumber() { if (serialNumber == null) { serialNumber = tbsCert.getSerialNumber(); @@ -150,54 +197,88 @@ return serialNumber; } + /** + * @see java.security.cert.X509Certificate#getIssuerDN() + * method documentation for more information. + */ public Principal getIssuerDN() { if (issuer == null) { + // retrieve the issuer's principal issuer = tbsCert.getIssuer().getX500Principal(); } return issuer; } + /** + * @see java.security.cert.X509Certificate#getIssuerX500Principal() + * method documentation for more information. + */ public X500Principal getIssuerX500Principal() { if (issuer == null) { + // retrieve the issuer's principal issuer = tbsCert.getIssuer().getX500Principal(); } return issuer; } - + + /** + * @see java.security.cert.X509Certificate#getSubjectDN() + * method documentation for more information. + */ public Principal getSubjectDN() { if (subject == null) { + // retrieve the subject's principal subject = tbsCert.getSubject().getX500Principal(); } return subject; } + /** + * @see java.security.cert.X509Certificate#getSubjectX500Principal() + * method documentation for more information. + */ public X500Principal getSubjectX500Principal() { if (subject == null) { + // retrieve the subject's principal subject = tbsCert.getSubject().getX500Principal(); } return subject; } + /** + * @see java.security.cert.X509Certificate#getNotBefore() + * method documentation for more information. + */ public Date getNotBefore() { if (notBefore == -1) { + // the value was not retrieved from the certificate, do it: notBefore = tbsCert.getValidity().getNotBefore().getTime(); notAfter = tbsCert.getValidity().getNotAfter().getTime(); } return new Date(notBefore); } + /** + * @see java.security.cert.X509Certificate#getNotAfter() + * method documentation for more information. + */ public Date getNotAfter() { if (notBefore == -1) { + // the value was not retrieved from the certificate, do it: notBefore = tbsCert.getValidity().getNotBefore().getTime(); notAfter = tbsCert.getValidity().getNotAfter().getTime(); } return new Date(notAfter); } + /** + * @see java.security.cert.X509Certificate#getTBSCertificate() + * method documentation for more information. + */ public byte[] getTBSCertificate() - throws CertificateEncodingException - { + throws CertificateEncodingException { if (tbsCertificate == null) { + // retrieve the encoded form of the TBSCertificate structure tbsCertificate = tbsCert.getEncoded(); } byte[] result = new byte[tbsCertificate.length]; @@ -205,8 +286,13 @@ return result; } + /** + * @see java.security.cert.X509Certificate#getSignature() + * method documentation for more information. + */ public byte[] getSignature() { if (signature == null) { + // retrieve the value of the signature signature = certificate.getSignatureValue(); } byte[] result = new byte[signature.length]; @@ -214,28 +300,46 @@ return result; } + /** + * @see java.security.cert.X509Certificate#getSigAlgName() + * method documentation for more information. + */ public String getSigAlgName() { if (sigAlgOID == null) { + // if info was not retrieved (and cached), do it: sigAlgOID = tbsCert.getSignature().getAlgorithm(); + // retrieve the name of the signing algorithm sigAlgName = AlgNameMapper.map2AlgName(sigAlgOID); if (sigAlgName == null) { + // if could not be found, use OID as a name sigAlgName = sigAlgOID; } } return sigAlgName; } + /** + * @see java.security.cert.X509Certificate#getSigAlgOID() + * method documentation for more information. + */ public String getSigAlgOID() { if (sigAlgOID == null) { + // if info was not retrieved (and cached), do it: sigAlgOID = tbsCert.getSignature().getAlgorithm(); + // retrieve the name of the signing algorithm sigAlgName = AlgNameMapper.map2AlgName(sigAlgOID); if (sigAlgName == null) { + // if could not be found, use OID as a name sigAlgName = sigAlgOID; } } return sigAlgOID; } + /** + * @see java.security.cert.X509Certificate#getSigAlgParams() + * method documentation for more information. + */ public byte[] getSigAlgParams() { if (nullSigAlgParams) { return null; @@ -251,21 +355,24 @@ } /** - * @return + * @see java.security.cert.X509Certificate#getIssuerUniqueID() + * method documentation for more information. */ public boolean[] getIssuerUniqueID() { return tbsCert.getIssuerUniqueID(); } /** - * @return + * @see java.security.cert.X509Certificate#getSubjectUniqueID() + * method documentation for more information. */ public boolean[] getSubjectUniqueID() { return tbsCert.getSubjectUniqueID(); } /** - * @return + * @see java.security.cert.X509Certificate#getKeyUsage() + * method documentation for more information. */ public boolean[] getKeyUsage() { if (extensions == null) { @@ -274,6 +381,10 @@ return extensions.valueOfKeyUsage(); } + /** + * @see java.security.cert.X509Certificate#getExtendedKeyUsage() + * method documentation for more information. + */ public List/**/ getExtendedKeyUsage() throws CertificateParsingException { if (extensions == null) { @@ -286,6 +397,10 @@ } } + /** + * @see java.security.cert.X509Certificate#getBasicConstraints() + * method documentation for more information. + */ public int getBasicConstraints() { if (extensions == null) { return Integer.MAX_VALUE; @@ -293,36 +408,53 @@ return extensions.valueOfBasicConstrains(); } + /** + * @see java.security.cert.X509Certificate#getSubjectAlternativeNames() + * method documentation for more information. + */ public Collection/*>*/ getSubjectAlternativeNames() throws CertificateParsingException { if (extensions == null) { return null; } try { + // Retrieve the extension value from the cached extensions object + // This extension is not checked for correctness during + // certificate generation, so now it can throw exception return extensions.valueOfSubjectAlternativeName(); } catch (IOException e) { throw new CertificateParsingException(e); } } + /** + * @see java.security.cert.X509Certificate#getIssuerAlternativeNames() + * method documentation for more information. + */ public Collection/*FIXME >*/ getIssuerAlternativeNames() throws CertificateParsingException { if (extensions == null) { return null; } try { + // Retrieve the extension value from the cached extensions object + // This extension is not checked for correctness during + // certificate generation, so now it can throw exception return extensions.valueOfIssuerAlternativeName(); } catch (IOException e) { throw new CertificateParsingException(e); } } - // + // // ----- java.security.cert.Certificate methods implementations ------ // - - public byte[] getEncoded() throws CertificateEncodingException - { + + /** + * @see java.security.cert.Certificate#getEncoded() + * method documentation for more information. + */ + public byte[] getEncoded() throws CertificateEncodingException { if (encoding == null) { encoding = certificate.getEncoded(); } @@ -331,102 +463,122 @@ return result; } + /** + * @see java.security.cert.Certificate#getPublicKey() + * method documentation for more information. + */ public PublicKey getPublicKey() { if (publicKey == null) { + // retrieve the public key from SubjectPublicKeyInfo + // substructure of X.509 certificate publicKey = tbsCert.getSubjectPublicKeyInfo().getPublicKey(); } return publicKey; } /** - * TODO: should be fully implemented. - * @return + * @see java.security.cert.Certificate#toString() + * method documentation for more information. */ public String toString() { return certificate.toString(); } - + /** - * TODO - * @param key: PublicKey - * @return - * @throwsCertificateException - * @throwsNoSuchAlgorithmException - * @throwsInvalidKeyException - * @throwsNoSuchProviderException - * @throwsSignatureException + * Verifies the signature of the certificate. + * @see java.security.cert.Certificate#verify(PublicKey) + * method documentation for more information. */ public void verify(PublicKey key) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, - SignatureException - { - Signature signature = Signature.getInstance( - tbsCert.getSignature().getAlgorithm()); + SignatureException { + Signature signature = Signature.getInstance(getSigAlgName()); signature.initVerify(key); - byte[] tbsCertEncoding = tbsCert.getEncoded(); - signature.update(tbsCertEncoding, 0, tbsCertEncoding.length); + // retrieve the ecnoding of the TBSCertificate structure + if (tbsCertificate == null) { + tbsCertificate = tbsCert.getEncoded(); + } + // compute and verify the signature + signature.update(tbsCertificate, 0, tbsCertificate.length); if (!signature.verify(certificate.getSignatureValue())) { throw new SignatureException("Signature was not verified."); } } /** - * TODO - * @param key: PublicKey - * @param sigProvider: String - * @return - * @throwsCertificateException - * @throwsNoSuchAlgorithmException - * @throwsInvalidKeyException - * @throwsNoSuchProviderException - * @throwsSignatureException + * Verifies the signature of the certificate. + * @see java.security.cert.Certificate#verify(PublicKey,String) + * method documentation for more information. */ public void verify(PublicKey key, String sigProvider) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, - SignatureException - { - Signature signature = Signature.getInstance( - tbsCert.getSignature().getAlgorithm(), sigProvider); + SignatureException { + Signature signature = + Signature.getInstance(getSigAlgName(), sigProvider); signature.initVerify(key); - byte[] tbsCertEncoding = tbsCert.getEncoded(); - signature.update(tbsCertEncoding, 0, tbsCertEncoding.length); + // retrieve the ecnoding of the TBSCertificate structure + if (tbsCertificate == null) { + tbsCertificate = tbsCert.getEncoded(); + } + // compute and verify the signature + signature.update(tbsCertificate, 0, tbsCertificate.length); if (!signature.verify(certificate.getSignatureValue())) { throw new SignatureException("Signature was not verified."); } } - // + // // ----- java.security.cert.X509Extension methods implementations ---- // + /** + * @see java.security.cert.X509Extension#getNonCriticalExtensionOIDs() + * method documentation for more information. + */ public Set getNonCriticalExtensionOIDs() { if (extensions == null) { return null; } + // retrieve the info from the cached extensions object return extensions.getNonCriticalExtensions(); } + /** + * @see java.security.cert.X509Extension#getCriticalExtensionOIDs() + * method documentation for more information. + */ public Set getCriticalExtensionOIDs() { if (extensions == null) { return null; } + // retrieve the info from the cached extensions object return extensions.getCriticalExtensions(); } + /** + * @see java.security.cert.X509Extension#getExtensionValue(String) + * method documentation for more information. + */ public byte[] getExtensionValue(String oid) { if (extensions == null) { return null; } + // retrieve the info from the cached extensions object Extension ext = extensions.getExtensionByOID(oid); return (ext == null) ? null : ext.getRawExtnValue(); } + /** + * @see java.security.cert.X509Extension#hasUnsupportedCriticalExtension() + * method documentation for more information. + */ public boolean hasUnsupportedCriticalExtension() { if (extensions == null) { return false; } + // retrieve the info from the cached extensions object return extensions.hasUnsupportedCritical(); } Index: modules/security/src/main/java/common/org/apache/harmony/security/provider/cert/DRLCertFactory.java =================================================================== --- modules/security/src/main/java/common/org/apache/harmony/security/provider/cert/DRLCertFactory.java (revision 410209) +++ modules/security/src/main/java/common/org/apache/harmony/security/provider/cert/DRLCertFactory.java (working copy) @@ -15,9 +15,9 @@ */ /** -* @author Alexander Y. Kleymenov -* @version $Revision$ -*/ + * @author Alexander Y. Kleymenov + * @version $Revision$ + */ package org.apache.harmony.security.provider.cert; @@ -26,7 +26,8 @@ /** - * DRLCertFactory + * Master class (provider) for X509 Certificate Factory + * Implementation. */ public final class DRLCertFactory extends Provider { @@ -35,12 +36,21 @@ */ private static final long serialVersionUID = -7269650779605195879L; + /** + * Constructs the instance of the certificate factory provider. + */ public DRLCertFactory() { - super("DRLCertFactory", 1.0, "DRL Certificate Factory"); + // specification of the provider name, version, and description. + super("DRLCertFactory", 1.0, + "Certificate Factory supports CRLs and Certificates " + + "in (PEM) ASN.1 DER encoded form, and Certification Paths " + + "in PkiPath and PKCS7 formats."); AccessController.doPrivileged(new java.security.PrivilegedAction() { public Object run() { - put("CertificateFactory.X509", + // register the service + put("CertificateFactory.X509", "org.apache.harmony.security.provider.cert.X509CertFactoryImpl"); + // mapping the alias put("Alg.Alias.CertificateFactory.X.509", "X509"); return null; } Index: modules/security/src/main/java/common/org/apache/harmony/security/provider/cert/X509CRLImpl.java =================================================================== --- modules/security/src/main/java/common/org/apache/harmony/security/provider/cert/X509CRLImpl.java (revision 410209) +++ modules/security/src/main/java/common/org/apache/harmony/security/provider/cert/X509CRLImpl.java (working copy) @@ -15,9 +15,9 @@ */ /** -* @author Alexander Y. Kleymenov -* @version $Revision$ -*/ + * @author Alexander Y. Kleymenov + * @version $Revision$ + */ package org.apache.harmony.security.provider.cert; @@ -50,39 +50,69 @@ import org.apache.harmony.security.x509.Extensions; import org.apache.harmony.security.x509.TBSCertList; - /** - * X509CRLImpl + * This class is an implementation of X509CRL. It wraps + * the instance of org.apache.harmony.security.x509.CertificateList + * built on the base of provided ASN.1 DER encoded form of + * CertificateList structure (as specified in RFC 3280 + * http://www.ietf.org/rfc/rfc3280.txt). + * Implementation supports work with indirect CRLs. + * @see org.apache.harmony.security.x509.CertificateList + * @see java.security.cert.X509CRL */ public class X509CRLImpl extends X509CRL { - + + // the core object to be wrapped in X509CRL private final CertificateList crl; + + // To speed up access to the info, the following fields + // cache values retrieved from the CertificateList object private final TBSCertList tbsCertList; + private byte[] tbsCertListEncoding; private final Extensions extensions; - - private boolean isIndirectCRL; - // cached values private X500Principal issuer; - private byte[] encoding; - private byte[] tbsCertListEncoding; private ArrayList entries; private int entriesSize; - private int nonIndirectEntriesSize; - private boolean entriesRetrieved; private byte[] signature; private String sigAlgOID; private String sigAlgName; private byte[] sigAlgParams; + + // encoded form of crl + private byte[] encoding; + + // indicates whether the signature algorithm parameters are null private boolean nullSigAlgParams; - + // indicates whether the crl entries have already been retrieved + // from CertificateList object (crl) + private boolean entriesRetrieved; + + // indicates whether this X.509 CRL is direct or indirect + // (see rfc 3280 http://www.ietf.org/rfc/rfc3280.txt, p 5.) + private boolean isIndirectCRL; + // if crl is indirect, this field holds an info about how + // many of the leading certificates in the list are issued + // by the same issuer as CRL. + private int nonIndirectEntriesSize; + + /** + * Creates X.509 CRL by wrapping of the specified CertificateList object. + */ public X509CRLImpl(CertificateList crl) { this.crl = crl; this.tbsCertList = crl.getTbsCertList(); this.extensions = tbsCertList.getCrlExtensions(); } + /** + * Creates X.509 CRL on the base of ASN.1 DER encoded form of + * the CRL (CertificateList structure described in RFC 3280) + * provided via input stream. + * @throws CRLException if decoding errors occur. + */ public X509CRLImpl(InputStream in) throws CRLException { try { + // decode CertificateList structure this.crl = (CertificateList) CertificateList.ASN1.decode(in); this.tbsCertList = crl.getTbsCertList(); this.extensions = tbsCertList.getCrlExtensions(); @@ -91,12 +121,23 @@ } } + /** + * Creates X.509 CRL on the base of ASN.1 DER encoded form of + * the CRL (CertificateList structure described in RFC 3280) + * provided via array of bytes. + * @throws IOException if decoding errors occur. + */ public X509CRLImpl(byte[] encoding) throws IOException { - this((CertificateList) CertificateList.ASN1.decode(encoding)); + this((CertificateList) CertificateList.ASN1.decode(encoding)); } + // --------------------------------------------------------------------- + // ----- java.security.cert.X509CRL abstract method implementations ---- + // --------------------------------------------------------------------- + /** - * getEncoded + * @see java.security.cert.X509CRL#getEncoded() + * method documentation for more info */ public byte[] getEncoded() throws CRLException { if (encoding == null) { @@ -108,14 +149,16 @@ } /** - * getVersion + * @see java.security.cert.X509CRL#getVersion() + * method documentation for more info */ public int getVersion() { return tbsCertList.getVersion(); } /** - * getIssuerDN + * @see java.security.cert.X509CRL#getIssuerDN() + * method documentation for more info */ public Principal getIssuerDN() { if (issuer == null) { @@ -125,7 +168,8 @@ } /** - * getIssuerX500Principal + * @see java.security.cert.X509CRL#getIssuerX500Principal() + * method documentation for more info */ public X500Principal getIssuerX500Principal() { if (issuer == null) { @@ -135,21 +179,26 @@ } /** - * getThisUpdate + * @see java.security.cert.X509CRL#getThisUpdate() + * method documentation for more info */ public Date getThisUpdate() { return tbsCertList.getThisUpdate(); } /** - * getNextUpdate + * @see java.security.cert.X509CRL#getNextUpdate() + * method documentation for more info */ public Date getNextUpdate() { return tbsCertList.getNextUpdate(); } - // Retrieves the crl entries and converts it to the X509CRLEntryImpl - // objects + /* + * Retrieves the crl entries (TBSCertList.RevokedCertificate objects) + * from the TBSCertList structure and converts them to the + * X509CRLEntryImpl objects + */ private void retirieveEntries() { entriesRetrieved = true; List rcerts = tbsCertList.getRevokedCertificates(); @@ -158,22 +207,34 @@ } entriesSize = rcerts.size(); entries = new ArrayList(entriesSize); - X500Principal rcertIssuer = null; // means that issuer is a CRL issuer + // null means that revoked certificate issuer is the same as CRL issuer + X500Principal rcertIssuer = null; for (int i=0; i*/ getRevokedCertificates() { + public Set getRevokedCertificates() { if (!entriesRetrieved) { retirieveEntries(); } @@ -249,20 +322,22 @@ } /** - * getTBSCertList + * @see java.security.cert.X509CRL#getTBSCertList() + * method documentation for more info */ public byte[] getTBSCertList() throws CRLException { if (tbsCertListEncoding == null) { tbsCertListEncoding = tbsCertList.getEncoded(); } byte[] result = new byte[tbsCertListEncoding.length]; - System.arraycopy(tbsCertListEncoding, 0, + System.arraycopy(tbsCertListEncoding, 0, result, 0, tbsCertListEncoding.length); return result; } /** - * getSignature + * @see java.security.cert.X509CRL#getSignature() + * method documentation for more info */ public byte[] getSignature() { if (signature == null) { @@ -274,7 +349,8 @@ } /** - * getSigAlgName + * @see java.security.cert.X509CRL#getSigAlgName() + * method documentation for more info */ public String getSigAlgName() { if (sigAlgOID == null) { @@ -288,7 +364,8 @@ } /** - * getSigAlgOID + * @see java.security.cert.X509CRL#getSigAlgOID() + * method documentation for more info */ public String getSigAlgOID() { if (sigAlgOID == null) { @@ -302,7 +379,8 @@ } /** - * getSigAlgParams + * @see java.security.cert.X509CRL#getSigAlgParams() + * method documentation for more info */ public byte[] getSigAlgParams() { if (nullSigAlgParams) { @@ -319,14 +397,14 @@ } /** - * verify + * @see java.security.cert.X509CRL#verify(PublicKey key) + * method documentation for more info */ public void verify(PublicKey key) throws CRLException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException { - Signature signature = Signature.getInstance( - tbsCertList.getSignature().getAlgorithm()); + Signature signature = Signature.getInstance(getSigAlgName()); signature.initVerify(key); byte[] tbsEncoding = tbsCertList.getEncoded(); signature.update(tbsEncoding, 0, tbsEncoding.length); @@ -336,14 +414,15 @@ } /** - * verify + * @see java.security.cert.X509CRL#verify(PublicKey key, String sigProvider) + * method documentation for more info */ public void verify(PublicKey key, String sigProvider) throws CRLException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException { Signature signature = Signature.getInstance( - tbsCertList.getSignature().getAlgorithm(), sigProvider); + getSigAlgName(), sigProvider); signature.initVerify(key); byte[] tbsEncoding = tbsCertList.getEncoded(); signature.update(tbsEncoding, 0, tbsEncoding.length); @@ -352,12 +431,13 @@ } } - // - // ----- java.security.cert.CRL methods implementations ------ - // - + // --------------------------------------------------------------------- + // ------ java.security.cert.CRL abstract method implementations ------- + // --------------------------------------------------------------------- + /** - * isRevoked + * @see java.security.cert.CRL#isRevoked(Certificate) + * method documentation for more info */ public boolean isRevoked(Certificate cert) { if (!(cert instanceof X509Certificate)) { @@ -367,17 +447,21 @@ } /** - * toString + * @see java.security.cert.CRL#toString() + * method documentation for more info */ public String toString() { - // FIXME - return "X509CRLImpl:..."; + return "X509CRLImpl: " + crl.toString(); } - // - // ----- java.security.cert.X509Extension methods implementations ---- - // + // --------------------------------------------------------------------- + // ------ java.security.cert.X509Extension method implementations ------ + // --------------------------------------------------------------------- + /** + * @see java.security.cert.X509Extension#getNonCriticalExtensionOIDs() + * method documentation for more info + */ public Set getNonCriticalExtensionOIDs() { if (extensions == null) { return null; @@ -385,6 +469,10 @@ return extensions.getNonCriticalExtensions(); } + /** + * @see java.security.cert.X509Extension#getCriticalExtensionOIDs() + * method documentation for more info + */ public Set getCriticalExtensionOIDs() { if (extensions == null) { return null; @@ -392,6 +480,10 @@ return extensions.getCriticalExtensions(); } + /** + * @see java.security.cert.X509Extension#getExtensionValue(String) + * method documentation for more info + */ public byte[] getExtensionValue(String oid) { if (extensions == null) { return null; @@ -400,6 +492,10 @@ return (ext == null) ? null : ext.getRawExtnValue(); } + /** + * @see java.security.cert.X509Extension#hasUnsupportedCriticalExtension() + * method documentation for more info + */ public boolean hasUnsupportedCriticalExtension() { if (extensions == null) { return false; @@ -407,3 +503,4 @@ return extensions.hasUnsupportedCritical(); } } + Index: modules/security/src/main/java/common/org/apache/harmony/security/provider/cert/X509CRLEntryImpl.java =================================================================== --- modules/security/src/main/java/common/org/apache/harmony/security/provider/cert/X509CRLEntryImpl.java (revision 410209) +++ modules/security/src/main/java/common/org/apache/harmony/security/provider/cert/X509CRLEntryImpl.java (working copy) @@ -15,9 +15,9 @@ */ /** -* @author Alexander Y. Kleymenov -* @version $Revision$ -*/ + * @author Alexander Y. Kleymenov + * @version $Revision$ + */ package org.apache.harmony.security.provider.cert; @@ -33,27 +33,51 @@ import org.apache.harmony.security.x509.Extensions; import org.apache.harmony.security.x509.TBSCertList; - /** - * X509CRLEntryImpl + * Implementation of X509CRLEntry. It wraps the instance + * of org.apache.harmony.security.x509.TBSCertList.RevokedCertificate + * obtained during the decoding of TBSCertList substructure + * of the CertificateList structure which is an X.509 form of CRL. + * (see RFC 3280 at http://www.ietf.org/rfc/rfc3280.txt) + * Normally the instances of this class are constructed by involving + * X509CRLImpl object. + * @see org.apache.harmony.security.x509.TBSCertList + * @see org.apache.harmony.security.provider.cert.X509CRLImpl + * @see java.security.cert.X509CRLEntry */ public class X509CRLEntryImpl extends X509CRLEntry { + // the crl entry object to be wrapped in X509CRLEntry private final TBSCertList.RevokedCertificate rcert; + // the extensions of the entry private final Extensions extensions; + // issuer of the revoked certificate described by this crl entry private final X500Principal issuer; + // encoded form of this revoked certificate entry private byte[] encoding; - - public X509CRLEntryImpl(TBSCertList.RevokedCertificate rcert, + + /** + * Creates an instance on the base of existing + * TBSCertList.RevokedCertificate object and + * information about the issuer of revoked certificate. + * If specified issuer is null, it is supposed that issuer + * of the revoked certificate is the same as for involving CRL. + */ + public X509CRLEntryImpl(TBSCertList.RevokedCertificate rcert, X500Principal issuer) { this.rcert = rcert; this.extensions = rcert.getCrlEntryExtensions(); this.issuer = issuer; } + // --------------------------------------------------------------------- + // ------ java.security.cert.X509CRLEntry method implementations ------- + // --------------------------------------------------------------------- + /** - * getEncoded + * @see java.security.cert.X509CRLEntry#getEncoded() + * method documentation for more info */ public byte[] getEncoded() throws CRLException { if (encoding == null) { @@ -65,43 +89,53 @@ } /** - * getSerialNumber + * @see java.security.cert.X509CRLEntry#getSerialNumber() + * method documentation for more info */ public BigInteger getSerialNumber() { return rcert.getUserCertificate(); } + /** + * @see java.security.cert.X509CRLEntry#getCertificateIssuer() + * method documentation for more info + */ public X500Principal getCertificateIssuer() { return issuer; } /** - * getRevocationDate + * @see java.security.cert.X509CRLEntry#getRevocationDate() + * method documentation for more info */ public Date getRevocationDate() { return rcert.getRevocationDate(); } /** - * @com.intel.drl.spec_ref + * @see java.security.cert.X509CRLEntry#hasExtensions() + * method documentation for more info */ public boolean hasExtensions() { return (extensions != null) && (extensions.size() != 0); } /** - * toString - * FIXME: recognize and print the extensions + * @see java.security.cert.X509CRLEntry#toString() + * method documentation for more info */ public String toString() { - // FIXME - return "X509CRLEntryImpl:..."; + return "X509CRLEntryImpl: "+rcert.toString(); } - // - // ----- java.security.cert.X509Extension methods implementations ---- - // + // --------------------------------------------------------------------- + // ------ java.security.cert.X509Extension method implementations ------ + // --------------------------------------------------------------------- + /** + * @see java.security.cert.X509Extension#getNonCriticalExtensionOIDs() + * method documentation for more info + */ public Set getNonCriticalExtensionOIDs() { if (extensions == null) { return null; @@ -109,6 +143,10 @@ return extensions.getNonCriticalExtensions(); } + /** + * @see java.security.cert.X509Extension#getCriticalExtensionOIDs() + * method documentation for more info + */ public Set getCriticalExtensionOIDs() { if (extensions == null) { return null; @@ -116,6 +154,10 @@ return extensions.getCriticalExtensions(); } + /** + * @see java.security.cert.X509Extension#getExtensionValue(String) + * method documentation for more info + */ public byte[] getExtensionValue(String oid) { if (extensions == null) { return null; @@ -124,17 +166,15 @@ return (ext == null) ? null : ext.getRawExtnValue(); } + /** + * @see java.security.cert.X509Extension#hasUnsupportedCriticalExtension() + * method documentation for more info + */ public boolean hasUnsupportedCriticalExtension() { if (extensions == null) { return false; } return extensions.hasUnsupportedCritical(); } +} - - /** - * The main method. - */ - public static void main(String[] args) { - } -} Index: modules/security/src/main/java/common/org/apache/harmony/security/provider/cert/X509CertFactoryImpl.java =================================================================== --- modules/security/src/main/java/common/org/apache/harmony/security/provider/cert/X509CertFactoryImpl.java (revision 410209) +++ modules/security/src/main/java/common/org/apache/harmony/security/provider/cert/X509CertFactoryImpl.java (working copy) @@ -15,9 +15,9 @@ */ /** -* @author Alexander Y. Kleymenov -* @version $Revision$ -*/ + * @author Alexander Y. Kleymenov + * @version $Revision$ + */ package org.apache.harmony.security.provider.cert; @@ -40,44 +40,77 @@ import org.apache.harmony.security.asn1.BerInputStream; /** - * X509CertFactoryImpl + * X509 Certificate Factory Service Provider Interface Implementation. + * It supports CRLs and Certificates in (PEM) ASN.1 DER encoded form, + * and Certification Paths in PkiPath and PKCS7 formats. + * For Certificates and CRLs factory maintains the caching + * mechanisms allowing to speed up repeated Certificate/CRL + * generation. + * @see Cache */ public class X509CertFactoryImpl extends CertificateFactorySpi { + // certificate cache private static Cache CERT_CASHE = new Cache(); + // crl cache, 24 leading/trailing bytes will be used for hash computation private static Cache CRL_CASHE = new Cache(24); - - public X509CertFactoryImpl() {} /** - * engineGenerateCertificate + * Default constructor. + * Creates the instance of Certificate Factory SPI ready for use. */ + public X509CertFactoryImpl() { } + + /** + * Generates the X.509 certificate from the data in the stream. + * The data in the stream can be either in ASN.1 DER encoded X.509 + * certificate, or PEM (Base64 encoding bounded by + * "-----BEGIN CERTIFICATE-----" at the beginning and + * "-----END CERTIFICATE-----" at the end) representation + * of the former encoded form. + * + * Before the generation the attempt the encoded form is looked up in + * the cache. If the cache contains the certificate with requested encoded + * form it is returned from it, otherwise it is generated by ASN.1 + * decoder. + * + * @see java.security.cert.CertificateFactorySpi#engineGenerateCertificate(InputStream) + * method documentation for more info + */ public Certificate engineGenerateCertificate(InputStream inStream) throws CertificateException { try { - if (inStream.markSupported()) { - } else { + if (!inStream.markSupported()) { + // create the mark supporting wrapper inStream = new RestoringInputStream(inStream); } + // mark is needed to recognize the format of the provided encoding + // (ASN.1 or PEM) inStream.mark(32); byte[] buff = new byte[28]; + // read the prefix of the encoding if (inStream.read(buff) < 28) { throw new CertificateException( "Input Stream contains not enought data."); } + // check whether the provided certificate is in PEM encoded form if ("-----BEGIN CERTIFICATE-----".equals(new String(buff, 0, 27))) { + // read PEM encoded form int size = inStream.available(); if (size == 0) { size = 2048; } buff = new byte[size]; int index=0, ch; + // read the Base64 encoded certificate into the buffer + // expect "-----END CERTIFICATE-----" at the end while ((ch = inStream.read()) != '-') { if (ch == -1) { throw new CertificateException( "Incorrect Base64 encoding: unexpected EOF."); } buff[index++] = (byte) ch; + // enlarge the buffer if needed if (index == size) { byte[] newbuff = new byte[size+1024]; System.arraycopy(buff, 0, newbuff, 0, size); @@ -86,46 +119,70 @@ } byte[] tmp = new byte[25]; inStream.read(tmp); + // check the trailing sequence if (!new String(tmp).startsWith("----END CERTIFICATE-----")) { throw new CertificateException( "Incorrect Base64 encoding: 'END CERTIFICATE' expected."); } // skip new line: set the position to the next certificate: inStream.mark(1); - while (((ch = inStream.read()) != -1) && (ch == '\n' || ch == '\r')) { + while (((ch = inStream.read()) != -1) + && (ch == '\n' || ch == '\r')) { inStream.mark(1); } inStream.reset(); + // retrieve the ASN.1 DER encoded form buff = Base64.decode(buff, index); if (buff == null) { - throw new CertificateException("Incorrect Base64 encoding."); + throw new CertificateException( + "Incorrect Base64 encoding."); } + // check whether certificate has already been generated and + // stored in the cache long hash = CERT_CASHE.getHash(buff); if (CERT_CASHE.contains(hash)) { + // preliminary check is successful, do more accurate check Certificate res = (Certificate) CERT_CASHE.get(hash, buff); if (res != null) { + // found in the cache return res; } } + // there is no generated certificate in the cache, + // so generate it Certificate res = new X509CertImpl(buff); + // put newly generated certificate in the cache CERT_CASHE.put(hash, buff, res); return res; } else { + // read ASN.1 DER encoded form inStream.reset(); + // check whether certificate has already been generated and + // stored in the cache long hash = CERT_CASHE.getHash(buff); if (CERT_CASHE.contains(hash)) { + // preliminary check is successful, do more accurate check. byte[] encoding = new byte[BerInputStream.getLength(buff)]; + // read full encoding form from the stream inStream.read(encoding); - Certificate res = + // try to retrieve from the cache + Certificate res = (Certificate) CERT_CASHE.get(hash, encoding); if (res != null) { + // found in the cache return res; } + // there is no generated certificate in the cache, + // so generate it res = new X509CertImpl(encoding); + // put newly generated certificate in the cache CERT_CASHE.put(hash, encoding, res); return res; } else { + // there is no generated certificate in the cache, + // so generate it Certificate res = new X509CertImpl(inStream); + // put newly generated certificate in the cache CERT_CASHE.put(hash, res.getEncoded(), res); return res; } @@ -134,62 +191,73 @@ throw new CertificateException(e); } } - + /** - * engineGenerateCertificates - * FIXME: 1.5 updates are needed Collection + * Generates the collection of the certificates on the base of provided + * via input stream encodings. + * @see java.security.cert.CertificateFactorySpi#engineGenerateCertificates(InputStream) + * method documentation for more info */ - public Collection engineGenerateCertificates(InputStream inStream) - throws CertificateException { + public Collection + engineGenerateCertificates(InputStream inStream) + throws CertificateException { ArrayList result = new ArrayList(); try { - if (inStream.markSupported()) { - } else { + if (!inStream.markSupported()) { + // create the mark supporting wrapper inStream = new RestoringInputStream(inStream); } inStream.mark(1); - // FIXME: Check if it is a PKCS7 structure, if not, do following: + // until the end of the stream is not reached .. while (inStream.read() != -1) { inStream.reset(); + // .. generate the certificate and add it to the resulting list result.add(engineGenerateCertificate(inStream)); inStream.mark(1); } } catch (IOException e) { - e.printStackTrace(); throw new CertificateException(e); } return result; } /** - * engineGenerateCRL + * @see java.security.cert.CertificateFactorySpi#engineGenerateCRL(InputStream) + * method documentation for more info */ public CRL engineGenerateCRL(InputStream inStream) throws CRLException { try { - if (inStream.markSupported()) { - } else { + if (!inStream.markSupported()) { + // create the mark supporting wrapper inStream = new RestoringInputStream(inStream); } + // mark is needed to recognize the format of the provided encoding + // (ASN.1 or PEM) inStream.mark(32); - byte[] buff = new byte[25]; // take one byte for new line + byte[] buff = new byte[25]; // take one byte for new line + // read the prefix of the encoding if (inStream.read(buff) < 25) { throw new CRLException( "Input Stream contains not enought data."); } + // check whether the provided crl is in PEM encoded form if ("-----BEGIN X509 CRL-----".equals(new String(buff, 0, 24))) { + // read PEM encoded form int size = inStream.available(); if (size == 0) { size = 1024; } buff = new byte[size]; int index=0, ch; + // read the Base64 encoded crl into the buffer while ((ch = inStream.read()) != '-') { if (ch == -1) { throw new CRLException( "Incorrect Base64 encoding: unexpected EOF."); } buff[index++] = (byte) ch; + // enlarge the buffer if needed if (index == size) { byte[] newbuff = new byte[size+1024]; System.arraycopy(buff, 0, newbuff, 0, size); @@ -228,7 +296,7 @@ if (CRL_CASHE.contains(hash)) { byte[] encoding = new byte[BerInputStream.getLength(buff)]; inStream.read(encoding); - CRL res = + CRL res = (CRL) CRL_CASHE.get(hash, encoding); if (res != null) { return res; @@ -248,18 +316,17 @@ } /** - * engineGenerateCRLs - * FIXME: 1.5 updates are needed Collection + * @see java.security.cert.CertificateFactorySpi#engineGenerateCRLs(InputStream) + * method documentation for more info */ - public Collection engineGenerateCRLs(InputStream inStream) + public Collection engineGenerateCRLs(InputStream inStream) throws CRLException { if (inStream == null) { throw new CRLException("Null input stream provided."); } ArrayList result = new ArrayList(); try { - if (inStream.markSupported()) { - } else { + if (!inStream.markSupported()) { inStream = new RestoringInputStream(inStream); } inStream.mark(1); @@ -277,7 +344,8 @@ } /** - * engineGenerateCertPath + * @see java.security.cert.CertificateFactorySpi#engineGenerateCertPath(InputStream) + * method documentation for more info */ public CertPath engineGenerateCertPath(InputStream inStream) throws CertificateException { @@ -285,15 +353,17 @@ } /** - * engineGenerateCertPath + * @see java.security.cert.CertificateFactorySpi#engineGenerateCertPath(InputStream,String) + * method documentation for more info */ - public CertPath engineGenerateCertPath(InputStream inStream, String encoding) - throws CertificateException { + public CertPath engineGenerateCertPath( + InputStream inStream, String encoding) throws CertificateException { return X509CertPathImpl.getInstance(inStream, encoding); } /** - * engineGenerateCertPath + * @see java.security.cert.CertificateFactorySpi#engineGenerateCertPath(List) + * method documentation for more info */ public CertPath engineGenerateCertPath(List certificates) throws CertificateException { @@ -301,41 +371,65 @@ } /** - * engineGetCertPathEncodings - * FIXME: 1.5 updates are needed Iterator + * @see java.security.cert.CertificateFactorySpi#engineGetCertPathEncodings() + * method documentation for more info */ - public Iterator engineGetCertPathEncodings() { + public Iterator engineGetCertPathEncodings() { return X509CertPathImpl.encodings.iterator(); } - /** - * Class represents the stream which supports reset to the - * marked state with readlimit == BUFF_SIZE. + /* + * This class extends any existing input stream with + * mark functionality. It acts as a wrapper over the + * stream and supports reset to the + * marked state with readlimit no more than BUFF_SIZE. */ private static class RestoringInputStream extends InputStream { + // wrapped input stream private final InputStream inStream; - private final static int BUFF_SIZE = 32; + // specifies how much of the read data is buffered + // after the mark has been set up + private static final int BUFF_SIZE = 32; + // buffer to keep the bytes read after the mark has been set up private final int[] buff = new int[BUFF_SIZE*2]; - // position in the buffer + // position of the next byte to read, + // the value of -1 indicates that the buffer is not used + // (mark was not set up or was invalidated, or reset to the marked + // position has been done and all the buffered data was read out) private int pos = -1; - // the last byte in the buffer + // position of the last buffered byte private int bar = 0; - // the last cell of the buffer + // position in the buffer where the mark becomes invalidated private int end = 0; - + + /** + * Creates the mark supporting wrapper over the stream. + */ public RestoringInputStream(InputStream inStream) { this.inStream = inStream; } + /** + * @see java.io.InputStream#available() + * method documentation for more info + */ public int available() throws IOException { return (bar - pos) + inStream.available(); } + /** + * @see java.io.InputStream#close() + * method documentation for more info + */ public void close() throws IOException { inStream.close(); } + /** + * @see java.io.InputStream#mark(int readlimit) + * method documentation for more info + */ public void mark(int readlimit) { if (pos < 0) { pos = 0; @@ -346,45 +440,85 @@ } } + /** + * @see java.io.InputStream#markSupported() + * method documentation for more info + */ public boolean markSupported() { return true; } + /** + * Reads the byte from the stream. If mark has been set up + * and was not invalidated byte is read from the underlying + * stream and saved into the buffer. If the current read position + * has been reset to the marked position and there are remaining + * bytes in the buffer, the byte is taken from it. In the other cases + * (if mark has been invalidated, or there are no buffered bytes) + * the byte is taken directly from the underlying stream and it is + * returned without saving to the buffer. + * + * @see java.io.InputStream#read() + * method documentation for more info + */ public int read() throws IOException { + // if buffer is currently used if (pos >= 0) { + // current position in the buffer int cur = pos % BUFF_SIZE; + // check whether the buffer contains the data to be read if (cur < bar) { + // return the data from the buffer pos++; return buff[cur]; } + // check whether buffer has free space if (cur != end) { + // it has, so read the data from the wrapped stream + // and place it in the buffer buff[cur] = inStream.read(); bar = cur+1; pos++; return buff[cur]; } else { - pos = -1; // can not operate anymore + // buffer if full and can not operate + // any more, so invalidate the mark position + // and turn off the using of buffer + pos = -1; } } + // buffer is not used, so return the data from the wrapped stream return inStream.read(); } + /** + * @see java.io.InputStream#read(byte[] b) + * method documentation for more info + */ public int read(byte[] b) throws IOException { return read(b, 0, b.length); } + /** + * @see java.io.InputStream#read(byte[] b, int off, int len) + * method documentation for more info + */ public int read(byte[] b, int off, int len) throws IOException { int read_b; int i; for (i=0; i= 0) { pos = (end + 1) % BUFF_SIZE; @@ -394,6 +528,10 @@ } } + /** + * @see java.io.InputStream#skip(long n) + * method documentation for more info + */ public long skip(long n) throws IOException { if (pos >= 0) { long i = 0;