diff options
author | PKI Team <PKI Team@c9f7a03b-bd48-0410-a16d-cbbf54688b0b> | 2008-03-18 22:36:57 +0000 |
---|---|---|
committer | PKI Team <PKI Team@c9f7a03b-bd48-0410-a16d-cbbf54688b0b> | 2008-03-18 22:36:57 +0000 |
commit | d0f2e4efbd3eb0f1d7f5a28e7f97c1fb4ec027bb (patch) | |
tree | 7e7473fae8af5ad7e6cda7eabbef787093fc59a7 /pki/base/util/src/netscape/security/x509 | |
parent | 273f8d85df5c31293a908185622b378c8f3cf7e8 (diff) | |
download | pki-d0f2e4efbd3eb0f1d7f5a28e7f97c1fb4ec027bb.tar.gz pki-d0f2e4efbd3eb0f1d7f5a28e7f97c1fb4ec027bb.tar.xz pki-d0f2e4efbd3eb0f1d7f5a28e7f97c1fb4ec027bb.zip |
Initial open source version based upon proprietary Red Hat Certificate System (RHCS) 7.3.
git-svn-id: svn+ssh://svn.fedorahosted.org/svn/pki/trunk@2 c9f7a03b-bd48-0410-a16d-cbbf54688b0b
Diffstat (limited to 'pki/base/util/src/netscape/security/x509')
102 files changed, 24949 insertions, 0 deletions
diff --git a/pki/base/util/src/netscape/security/x509/ACertAttrSet.java b/pki/base/util/src/netscape/security/x509/ACertAttrSet.java new file mode 100755 index 00000000..5c1f25ce --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/ACertAttrSet.java @@ -0,0 +1,139 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.cert.CertificateException; +import java.util.Enumeration; +import netscape.security.util.DerValue; +import netscape.security.util.DerOutputStream; + +/** + * A plain certattr set used by pkcs10 to parse an unknown attribute. + * @author Lily Hsiao + */ +public class ACertAttrSet implements CertAttrSet { + + protected DerValue mDerValue = null; + + public ACertAttrSet(DerValue derValue) throws IOException { + mDerValue = derValue; + } + + public DerValue getDerValue() { + return mDerValue; + } + + /** + * Returns a short string describing this certificate attribute. + * + * @return value of this certificate attribute in + * printable form. + */ + public String toString() { + return "ACertAttrSet value "+ (mDerValue == null ? "null" : "not null"); + } + + /** + * Encodes the attribute to the output stream in a format + * that can be parsed by the <code>decode</code> method. + * + * @param out the OutputStream to encode the attribute to. + * + * @exception CertificateException on encoding or validity errors. + * @exception IOException on other errors. + */ + public void encode(OutputStream out) + throws CertificateException, IOException { + mDerValue.encode((DerOutputStream)out); + } + + /** + * Decodes the attribute in the input stream. + * + * @param in the InputStream to read the encoded attribute from. + * + * @exception CertificateException on decoding or validity errors. + * @exception IOException on other errors. + */ + public void decode(InputStream in) + throws CertificateException, IOException { + throw new IOException("not supported"); + } + + /** + * Sets an attribute value within this CertAttrSet. + * + * @param name the name of the attribute (e.g. "x509.info.key") + * @param obj the attribute object. + * + * @exception CertificateException on attribute handling errors. + * @exception IOException on other errors. + */ + public void set(String name, Object obj) + throws CertificateException, IOException { + throw new IOException("not supported"); + } + + /** + * Gets an attribute value for this CertAttrSet. + * + * @param name the name of the attribute to return. + * + * @exception CertificateException on attribute handling errors. + * @exception IOException on other errors. + */ + public Object get(String name) + throws CertificateException, IOException { + throw new IOException("not supported"); + } + + /** + * Deletes an attribute value from this CertAttrSet. + * + * @param name the name of the attribute to delete. + * + * @exception CertificateException on attribute handling errors. + * @exception IOException on other errors. + */ + public void delete(String name) + throws CertificateException, IOException { + throw new IOException("not supported"); + } + + /** + * Returns an enumeration of the names of the attributes existing within + * this attribute. + * + * @return an enumeration of the attribute names. + */ + public Enumeration getElements() { + return null; + } + + /** + * Returns the name (identifier) of this CertAttrSet. + * + * @return the name of this CertAttrSet. + */ + public String getName() { + return "Generic Extension"; + } +} diff --git a/pki/base/util/src/netscape/security/x509/AVA.java b/pki/base/util/src/netscape/security/x509/AVA.java new file mode 100644 index 00000000..9609d790 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/AVA.java @@ -0,0 +1,308 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.StringReader; +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; +import sun.io.ByteToCharConverter; + +import netscape.security.util.*; + + +/** + * X.500 Attribute-Value-Assertion (AVA): an attribute, as identified by + * some attribute ID, has some particular value. Values are as a rule ASN.1 + * printable strings. A conventional set of type IDs is recognized when + * parsing (and generating) RFC 1779 syntax strings. + * + * <P>AVAs are components of X.500 relative names. Think of them as being + * individual fields of a database record. The attribute ID is how you + * identify the field, and the value is part of a particular record. + * + * @see X500Name + * @see RDN + * @see LdapDNStrConverter + * + * @version 1.14 + * + * @author David Brownell + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +// public ... when RDN is public and X.500Names can be +// constructed using RDNs, and all three classes are cleaner +public final class AVA implements DerEncoder +{ + ObjectIdentifier oid; + DerValue value; + + /** + * Constructs an AVA from a Ldap DN string with one AVA component + * using the global default LdapDNStrConverter. + * @see LdapDNStrConverter + * @param avaString a Ldap DN string with one AVA component. + */ + public AVA(String avaString) + throws IOException + { + AVA ava; + ava = LdapDNStrConverter.getDefault().parseAVA(avaString); + oid = ava.getOid(); + value = ava.getValue(); + } + + /** + * Like AVA(String) with a DER encoding order given for Directory Strings. + */ + public AVA(String avaString, byte[] tags) + throws IOException + { + AVA ava; + ava = LdapDNStrConverter.getDefault().parseAVA(avaString, tags); + oid = ava.getOid(); + value = ava.getValue(); + } + + /** + * Constructs an AVA from a Ldap DN string containing one AVA + * component using the specified LdapDNStrConverter. + * @see LdapDNStrConverter + * @param avaString a Ldap DN string containing one AVA. + * @param ldapDNStrConverter a LdapDNStrConverter + */ + public AVA(String avaString, LdapDNStrConverter ldapDNStrConverter) + throws IOException + { + AVA ava; + ava = ldapDNStrConverter.parseAVA(avaString); + oid = ava.getOid(); + value = ava.getValue(); + } + + /** + * Constructs an AVA from an OID and DerValue. + * @param type an ObjectIdentifier + * @param val a DerValue + */ + public AVA(ObjectIdentifier type, DerValue val) + throws IOException + { + oid = type; + value = val; + } + + /** + * Constructs an AVA from an input stream of UTF8 bytes that form + * a Ldap DN string. Then parse the Ldap DN string using the global + * default LdapDNStrConverter. + * <br> + * Parses an RFC 1779 style AVA string: CN=fee fie foe fum + * or perhaps with quotes. Not all defined AVA tags are supported; + * of current note are X.400 related ones (PRMD, ADMD, etc). + * + * This terminates at unescaped AVA separators ("+") or RDN + * separators (",", ";"), or DN terminators (">"), and removes + * cosmetic whitespace at the end of values. + * + * @see LdapDNStrConverter + * @param in the input stream. + */ + public AVA (InputStream in) throws IOException + { + StringBuffer temp = new StringBuffer (); + AVA a; + byte[] buf = new byte[in.available()]; + char[] converted_chars; + ByteToCharConverter bcc; + + // convert from UTF8 bytes to java string then parse it. + in.read(buf); + try { + bcc = ByteToCharConverter.getConverter("UTF8"); + } + catch (java.io.UnsupportedEncodingException e) { + throw new IOException("UTF8 encoding not supported"); + } + converted_chars = bcc.convertAll(buf); + temp.append(converted_chars); + + a = LdapDNStrConverter.getDefault().parseAVA(temp.toString()); + oid = a.getOid(); + value = a.getValue(); + } + + /** + * Constructs an AVA from a Der Input Stream. + * @param in the Der Input Stream. + */ + public AVA(DerInputStream in) throws IOException + { + DerValue assertion = in.getDerValue (); + + /* + * Individual attribute value assertions are SEQUENCE of two values. + * That'd be a "struct" outside of ASN.1. + */ + if (assertion.tag != DerValue.tag_Sequence) + throw new CertParseError ("X500 AVA, not a sequence"); + + ObjectIdentifier o = assertion.data.getOID(); + oid = X500NameAttrMap.getDefault().getOid(o); + if (oid == null) { + // NSCP #329837 + // if this OID is not recongized in our map (table), + // it is fine. we just store it as regular OID. + oid = o; + } + value = assertion.data.getDerValue (); + + if (assertion.data.available () != 0) + throw new CertParseError ("AVA, extra bytes = " + + assertion.data.available ()); + } + + // other public methods. + + /** + * Returns true if another AVA has the same OID and DerValue. + * @param other the other AVA. + * @return ture iff other AVA has same oid and value. + */ + public boolean equals (AVA other) + { + return oid.equals (other.oid) && value.equals (other.value); + } + + /** + * Compares the AVA with an Object, returns true if the object is + * an AVA and has the same OID and value. + * @param other the other object. + * @return true iff other object is an AVA and has same oid and value. + */ + public boolean equals (Object other) + { + if (other instanceof AVA) + return equals ((AVA)other); + else + return false; + } + + /** + * Encodes the AVA to a Der output stream. + * AVAs are encoded as a SEQUENCE of two elements. + * @param out The Der output stream. + */ + public void encode (DerOutputStream out) throws IOException + { + derEncode(out); + } + + /** + * DER encode this object onto an output stream. + * Implements the <code>DerEncoder</code> interface. + * + * @param out + * the output stream on which to write the DER encoding. + * + * @exception IOException on encoding error. + */ + public void derEncode (OutputStream out) throws IOException + { + DerOutputStream tmp = new DerOutputStream (); + DerOutputStream tmp2 = new DerOutputStream (); + + tmp.putOID (oid); + value.encode (tmp); + tmp2.write (DerValue.tag_Sequence, tmp); + out.write(tmp2.toByteArray()); + } + + /** + * Returns a Ldap DN string with one AVA component using + * the global default LdapDNStrConverter. + * @return a Ldap DN string + * @exception IOException if an error occurs during conversion. + * @see LdapDNStrConverter + */ + public String toLdapDNString() + throws IOException + { + LdapDNStrConverter v = LdapDNStrConverter.getDefault(); + return v.encodeAVA(this); + } + + /** + * Returns a Ldap DN string with one AVA component using the specified + * LdapDNStrConverter. + * @return a Ldap DN string + * @param ldapDNStrConverter a Ldap DN String Converter + * @exception IOException if an error occurs during the conversion. + * @see LdapDNStrConverter + */ + public String toLdapDNString(LdapDNStrConverter ldapDNStrConverter) + throws IOException + { + return ldapDNStrConverter.encodeAVA(this); + } + + /** + * Returns a Ldap DN string with the AVA component using the global + * default LdapDNStrConverter, or null if an error occurs in conversion. + * @return a Ldap DN string containing the AVA, or null if an + * error occurs in the conversion. + */ + public String toString() + { + String s; + try { + // NOTE that a LdapDNString is returned here to match the + // original source from sun. Could also return the raw value + // (before Ldap escaping) here. + s = toLdapDNString(); + } + catch (IOException e) { + return null; + } + return s; + } + + /** + * Returns the OID in the AVA. + * @return the ObjectIdentifier in this AVA. + */ + public ObjectIdentifier getOid() + { + return oid; + } + + /** + * Returns the value in this AVA as a DerValue + * @return attribute value in this AVA. + */ + public DerValue getValue() + { + return value; + } + +} + + diff --git a/pki/base/util/src/netscape/security/x509/AVAValueConverter.java b/pki/base/util/src/netscape/security/x509/AVAValueConverter.java new file mode 100644 index 00000000..18de32c2 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/AVAValueConverter.java @@ -0,0 +1,89 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.*; + +import netscape.security.util.DerValue; + +/** + * Interface for classes that convert a attribute value string to a + * DER encoded ASN.1 value and vice versa. + * The converters are associated with attribute types, such as + * directory string, ia5string, etc. + * + * <P>For example, to convert a string, such as an organization name for the + * "O" attribute to a DerValue, the "O" attribute is mapped to the + * DirStrConverter which is used to convert the organization name to a + * DER encoded Directory String which is a DerValue of a ASN.1 PrintableString, + * T.61String or UniversalString for the organization name. + * + * @author Lily Hsiao, Slava Galperin at Netscape Communications, Inc. + */ + +public interface AVAValueConverter +{ + /** + * Converts a string to a DER encoded attribute value. + * + * @param valueString An AVA value string not encoded in any form. + * + * @return A DerValue object. + * + * @exception IOException if an error occurs during the conversion. + */ + public DerValue getValue(String valueString) + throws IOException; + + + /** + * Converts a string to a DER encoded attribute value. + * Specify the order of DER tags to use if more than one encoding is + * possible. Currently Directory Strings can have different order + * for backwards compatibility. By 2003 all should be UTF8String. + * + * @param valueString An AVA value string not encoded in any form. + * + * @return A DerValue object. + * + * @exception IOException if an error occurs during the conversion. + */ + public DerValue getValue(String valueString, byte[] tags) + throws IOException; + + /** + * Converts a BER encoded value to a DER encoded attribute value. + * + * @param berStream A byte array of the BER encoded AVA value. + * @return A DerValue object. + */ + public DerValue getValue(byte[] berStream) + throws IOException; + + /** + * Converts a DER encoded value to a string, not encoded in any form. + * + * @param avaValue A DerValue object. + * + * @return A string for the value or null if it can't be converted. + * + * @exception IOException if an error occurs during the conversion. + */ + public String getAsString(DerValue avaValue) + throws IOException; +} diff --git a/pki/base/util/src/netscape/security/x509/AlgIdDSA.java b/pki/base/util/src/netscape/security/x509/AlgIdDSA.java new file mode 100644 index 00000000..a3e638eb --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/AlgIdDSA.java @@ -0,0 +1,178 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.*; +import java.security.interfaces.DSAParams; + +import netscape.security.util.*; + + +/** + * This class identifies DSS/DSA Algorithm variants, which are distinguished + * by using different algorithm parameters <em>P, Q, G</em>. It uses the + * NIST/IETF standard DER encoding. These are used to implement the Digital + * Signature Standard (DSS), FIPS 186. + * + * <P><em><b>NOTE:</b> At this time, DSS/DSA Algorithm IDs must always + * include these parameters. Use of DSS/DSA in modes where parameters are + * either implicit (e.g. a default applicable to a site or a larger scope), + * or are derived from some Certificate Authority's DSS certificate, is + * not currently supported. </em> + * + * @version 1.31 + * @author David Brownell + */ +public final +class AlgIdDSA extends AlgorithmId implements DSAParams +{ + /* + * The three unsigned integer parameters. + */ + private BigInteger p , q, g; + + /** Returns the DSS/DSA parameter "P" */ + public BigInteger getP () { return p; } + + /** Returns the DSS/DSA parameter "Q" */ + public BigInteger getQ () { return q; } + + /** Returns the DSS/DSA parameter "G" */ + public BigInteger getG () { return g; } + + /** + * Default constructor. The OID and parameters must be + * deserialized before this algorithm ID is used. + */ + // XXX deprecated for general use + public AlgIdDSA () {} + + AlgIdDSA (DerValue val) throws IOException + { super(val.getOID()); } + + /** + * Construct an AlgIdDSA from an X.509 encoded byte array. + */ + public AlgIdDSA (byte[] encodedAlg) throws IOException + { super (new DerValue(encodedAlg).getOID()); } + + /** + * Constructs a DSS/DSA Algorithm ID from unsigned integers that + * define the algorithm parameters. Those integers are encoded + * as big-endian byte arrays. + * + * @param p the DSS/DSA paramter "P" + * @param q the DSS/DSA paramter "Q" + * @param g the DSS/DSA paramter "G" + */ + public AlgIdDSA (byte p [], byte q [], byte g []) + throws IOException + { + this (new BigInteger (1, p), + new BigInteger (1, q), + new BigInteger (1, g)); + } + + /** + * Constructs a DSS/DSA Algorithm ID from numeric parameters. + * + * @param p the DSS/DSA paramter "P" + * @param q the DSS/DSA paramter "Q" + * @param g the DSS/DSA paramter "G" + */ + public AlgIdDSA (BigInteger p, BigInteger q, BigInteger g) + { + super (DSA_oid); + + try { + this.p = p; + this.q = q; + this.g = g; + initializeParams (); + + } catch (IOException e) { + /* this should not happen */ + throw new ProviderException ("Construct DSS/DSA Algorithm ID"); + } + } + + /** + * Returns "DSA", indicating the Digital Signature Algorithm (DSA) as + * defined by the Digital Signature Standard (DSS), FIPS 186. + */ + public String getName () + { return "DSA"; } + + + /* + * For algorithm IDs which haven't been created from a DER encoded + * value, "params" must be created. + */ + private void initializeParams () + throws IOException + { + DerOutputStream out = new DerOutputStream (); + + out.putInteger (new BigInt(p.toByteArray())); + out.putInteger (new BigInt(q.toByteArray())); + out.putInteger (new BigInt(g.toByteArray())); + params = new DerValue (DerValue.tag_Sequence,out.toByteArray ()); + } + + /** + * Parses algorithm parameters P, Q, and G. They're found + * in the "params" member, which never needs to be changed. + */ + protected void decodeParams () + throws IOException + { + if (params == null || params.tag != DerValue.tag_Sequence) + throw new IOException("DSA alg parsing error"); + + params.data.reset (); + + this.p = params.data.getInteger ().toBigInteger(); + this.q = params.data.getInteger ().toBigInteger(); + this.g = params.data.getInteger ().toBigInteger(); + + if (params.data.available () != 0) + throw new IOException ("AlgIdDSA params, extra="+ + params.data.available ()); + } + + + /* + * Returns a formatted string describing the parameters. + */ + public String toString () + { return paramsToString (); } + + /* + * Returns a string describing the parameters. + */ + protected String paramsToString () + { + return + "\n p:\n" + (new BigInt(p)).toString() + + "\n q:\n" + (new BigInt(q)).toString() + + "\n g:\n" + (new BigInt(g)).toString() + + "\n"; + } +} diff --git a/pki/base/util/src/netscape/security/x509/AlgorithmId.java b/pki/base/util/src/netscape/security/x509/AlgorithmId.java new file mode 100644 index 00000000..28f6fa49 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/AlgorithmId.java @@ -0,0 +1,751 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.*; +import java.security.*; +import java.util.Properties; + +import netscape.security.util.*; + + +/** + * This class identifies algorithms, such as cryptographic transforms, each + * of which may be associated with parameters. Instances of this base class + * are used when this runtime environment has no special knowledge of the + * algorithm type, and may also be used in other cases. Equivalence is + * defined according to OID and (where relevant) parameters. + * + * <P>Subclasses may be used, for example when when the algorithm ID has + * associated parameters which some code (e.g. code using public keys) needs + * to have parsed. Two examples of such algorithms are Diffie-Hellman key + * exchange, and the Digital Signature Standard Algorithm (DSS/DSA). + * + * <P>The OID constants defined in this class correspond to some widely + * used algorithms, for which conventional string names have been defined. + * This class is not a general repository for OIDs, or for such string names. + * Note that the mappings between algorithm IDs and algorithm names is + * not one-to-one. + * + * @version 1.70 + * + * @author David Brownell + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class AlgorithmId implements Serializable, DerEncoder { + + /** use serialVersionUID from JDK 1.1. for interoperability */ + private static final long serialVersionUID = 7205873507486557157L; + + /* Are we debugging? */ + private static boolean debug = false; + + /** + * The object identitifer being used for this algorithm. + */ + private ObjectIdentifier algid = null; + + // The (parsed) parameters + private AlgorithmParameters algParams; + + /** + * Parameters for this algorithm. These are stored in unparsed + * DER-encoded form; subclasses can be made to automaticaly parse + * them so there is fast access to these parameters. + */ + protected DerValue params = null; + + + /** + * Returns one of the algorithm IDs most commonly associated + * with this algorithm name. + * + * @param algname the name being used + * @deprecated use the short get form of this method. + * @exception NoSuchAlgorithmException on error. + */ + public static AlgorithmId getAlgorithmId(String algname) + throws NoSuchAlgorithmException + { + return get(algname); + } + + public AlgorithmParameters getParameters() { + return this.algParams; + } + + /** + * Returns one of the algorithm IDs most commonly associated + * with this algorithm name. + * + * @param algname the name being used + * @exception NoSuchAlgorithmException on error. + */ + public static AlgorithmId get(String algname) + throws NoSuchAlgorithmException + { + ObjectIdentifier oid = algOID(algname); + + if (oid == null) + throw new NoSuchAlgorithmException + ("unrecognized algorithm name: " + algname); + + return new AlgorithmId(oid); + } + + /** + * Parse (unmarshal) an ID from a DER sequence input value. This form + * parsing might be used when expanding a value which has already been + * partially unmarshaled as a set or sequence member. + * + * @exception IOException on error. + * @param val the input value, which contains the algid and, if + * there are any parameters, those parameters. + * @return an ID for the algorithm. If the system is configured + * appropriately, this may be an instance of a class + * with some kind of special support for this algorithm. + * In that case, you may "narrow" the type of the ID. + */ + public static AlgorithmId parse(DerValue val) + throws IOException + { + if (val.tag != DerValue.tag_Sequence) + throw new IOException("algid parse error, not a sequence"); + + /* + * Get the algorithm ID and any parameters. + */ + ObjectIdentifier algid; + DerValue params; + DerInputStream in = val.toDerInputStream(); + + algid = in.getOID(); + if (in.available() == 0) + params = null; + else { + params = in.getDerValue(); + if (params.tag == DerValue.tag_Null) + params = null; + } + + /* + * Figure out what class (if any) knows about this oid's + * parameters. Make one, and give it the data to decode. + */ + AlgorithmId alg = new AlgorithmId(algid, params); + if (params != null) + alg.decodeParams(); + return alg; + } + + public static AlgorithmId parse(byte[] val) + throws IOException + { + return null; + } + + /** + * Constructs a parameterless algorithm ID. + * + * @param oid the identifier for the algorithm + */ + public AlgorithmId(ObjectIdentifier oid) { + algid = oid; + } + + + private AlgorithmId(ObjectIdentifier oid, DerValue params) + throws IOException { + this.algid = oid; + this.params = params; + if (this.params != null) + decodeParams(); + } + + + /** + * Constructs an algorithm ID which will be initialized + * separately, for example by deserialization. + * @deprecated use one of the other constructors. + */ + public AlgorithmId() { } + + + protected void decodeParams() throws IOException { + try { + this.algParams = AlgorithmParameters.getInstance + (this.algid.toString()); + } catch (NoSuchAlgorithmException e) { + /* + * This algorithm parameter type is not supported, so we cannot + * parse the parameters. + */ + this.algParams = null; + return; + } + // Decode (parse) the parameters + this.algParams.init(this.params.toByteArray()); + } + + /** + * Marshal a DER-encoded "AlgorithmID" sequence on the DER stream. + */ + public final void encode(DerOutputStream out) + throws IOException + { + derEncode(out); + } + + /** + * DER encode this object onto an output stream. + * Implements the <code>DerEncoder</code> interface. + * + * @param out + * the output stream on which to write the DER encoding. + * + * @exception IOException on encoding error. + */ + public void derEncode (OutputStream out) throws IOException + { + DerOutputStream bytes = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + + bytes.putOID(algid); + if (params == null) + bytes.putNull(); + else + bytes.putDerValue(params); + tmp.write(DerValue.tag_Sequence, bytes); + out.write(tmp.toByteArray()); + } + +// XXXX cleaning required + /** + * Returns the DER-encoded X.509 AlgorithmId as a byte array. + */ + public final byte[] encode() throws IOException + { + DerOutputStream out = new DerOutputStream (); + DerOutputStream bytes = new DerOutputStream (); + + bytes.putOID(algid); + if (params == null) + bytes.putNull(); + else + bytes.putDerValue(params); + out.write(DerValue.tag_Sequence, bytes); + return out.toByteArray(); + } + + /** + * Returns list of signing algorithms for a key algorithm such as + * RSA or DSA. + */ + public static String[] getSigningAlgorithms(AlgorithmId alg) { + ObjectIdentifier algOid = alg.getOID(); + //System.out.println("Key Alg oid "+algOid.toString()); + if (algOid.equals(DSA_oid) || algOid.equals(DSA_OIW_oid)) { + return DSA_SIGNING_ALGORITHMS; + } else if (algOid.equals(RSA_oid) || algOid.equals(RSAEncryption_oid)) { + return RSA_SIGNING_ALGORITHMS; + } else if (algOid.equals(ANSIX962_EC_Public_Key_oid) || algOid.equals(ANSIX962_SHA1_With_EC_oid)) { + return EC_SIGNING_ALGORITHMS; + } else { + return null; + } + } + + /* + * Translates from some common algorithm names to the + * OID with which they're usually associated ... this mapping + * is the reverse of the one below, except in those cases + * where synonyms are supported or where a given algorithm + * is commonly associated with multiple OIDs. + */ + private static ObjectIdentifier algOID (String name) + { + // Digesting algorithms + + if (name.equals ("MD5")) + return AlgorithmId.MD5_oid; + if (name.equals ("MD2")) + return AlgorithmId.MD2_oid; + if (name.equals ("SHA") || name.equals ("SHA1") + || name.equals("SHA-1")) + return AlgorithmId.SHA_oid; + if (name.equals ("SHA256") || name.equals("SHA-256")) + return AlgorithmId.SHA256_oid; + if (name.equals("SHA512") || name.equals("SHA-512")) + return AlgorithmId.SHA512_oid; + + // Various public key algorithms + + if (name.equals ("RSA")) + return AlgorithmId.RSA_oid; + + if (name.equals ("RSAEncryption")) + return AlgorithmId.RSAEncryption_oid; + if (name.equals ("Diffie-Hellman") || name.equals("DH")) + return AlgorithmId.DH_oid; + if (name.equals ("DSA")) + return AlgorithmId.DSA_oid; + + // Common signature types + + if (name.equals ("SHA1withEC") || name.equals("SHA1/EC") + || name.equals("1.2.840.10045.4.1")) + return AlgorithmId.sha1WithEC_oid; + if (name.equals ("SHA1withRSA") || name.equals("SHA1/RSA") + || name.equals("1.2.840.113549.1.1.5")) + return AlgorithmId.sha1WithRSAEncryption_oid; + if (name.equals ("SHA256withRSA") || name.equals("SHA256/RSA") + || name.equals("1.2.840.113549.1.1.11")) + return AlgorithmId.sha256WithRSAEncryption_oid; + if (name.equals ("SHA512withRSA") || name.equals("SHA512/RSA") + || name.equals("1.2.840.113549.1.1.13")) + return AlgorithmId.sha512WithRSAEncryption_oid; + if (name.equals ("MD5withRSA") || name.equals("MD5/RSA")) + return AlgorithmId.md5WithRSAEncryption_oid; + if (name.equals ("MD2withRSA") || name.equals("MD2/RSA")) + return AlgorithmId.md2WithRSAEncryption_oid; + if (name.equals("SHAwithDSA") || name.equals("SHA1withDSA") + || name.equals("SHA/DSA") || name.equals("SHA1/DSA")) + return AlgorithmId.sha1WithDSA_oid; + + return null; + } + + /* + * For the inevitable cases where key or signature types are not + * configured in an environment which encounters such keys or + * signatures, we still attempt to provide user-friendly names + * for some of the most common algorithms. Subclasses can of + * course override getName(). + * + * Wherever possible, the names are those defined by the IETF. + * Such names are noted below. + */ + private String algName() + { + // Common message digest algorithms + + if (algid.equals(AlgorithmId.MD5_oid)) + return "MD5"; // RFC 1423 + if (algid.equals(AlgorithmId.MD2_oid)) + return "MD2"; // RFC 1423 + if (algid.equals(AlgorithmId.SHA_oid)) + return "SHA"; + if (algid.equals(AlgorithmId.SHA256_oid)) + return "SHA256"; + if (algid.equals(AlgorithmId.SHA512_oid)) + return "SHA512"; + + // Common key types + + if (algid.equals(AlgorithmId.ANSIX962_EC_Public_Key_oid)) + return "EC"; + if (algid.equals(AlgorithmId.RSAEncryption_oid) + || algid.equals(AlgorithmId.RSA_oid)) + return "RSA"; + if (algid.equals(AlgorithmId.DH_oid) + || algid.equals(AlgorithmId.DH_PKIX_oid)) + return "Diffie-Hellman"; + if (algid.equals(AlgorithmId.DSA_oid) + || algid.equals(AlgorithmId.DSA_OIW_oid)) + return "DSA"; + + // Common signature types + + if (algid.equals (AlgorithmId.sha1WithEC_oid)) + return "SHA1withEC"; + if (algid.equals (AlgorithmId.md5WithRSAEncryption_oid)) + return "MD5withRSA"; + if (algid.equals (AlgorithmId.md2WithRSAEncryption_oid)) + return "MD2withRSA"; + if (algid.equals (AlgorithmId.sha1WithRSAEncryption_oid)) + return "SHA1withRSA"; + if (algid.equals (AlgorithmId.sha256WithRSAEncryption_oid)) + return "SHA256withRSA"; + if (algid.equals (AlgorithmId.sha512WithRSAEncryption_oid)) + return "SHA512withRSA"; + if (algid.equals(AlgorithmId.sha1WithDSA_oid) + || algid.equals(AlgorithmId.sha1WithDSA_OIW_oid) + || algid.equals(AlgorithmId.shaWithDSA_OIW_oid)) + return "SHA1withDSA"; + + // default returns a dot-notation ID + + return "OID." + algid.toString (); + } + + /** + * Returns the ISO OID for this algorithm. This is usually converted + * to a string and used as part of an algorithm name, for example + * "OID.1.3.14.3.2.13" style notation. Use the <code>getName</code> + * call when you do not need to ensure cross-system portability + * of algorithm names, or need a user friendly name. + */ + final public ObjectIdentifier getOID () + { return algid; } + + + /** + * Returns a name for the algorithm which may be more intelligible + * to humans than the algorithm's OID, but which won't necessarily + * be comprehensible on other systems. For example, this might + * return a name such as "MD5withRSA" for a signature algorithm on + * some systems. It also returns names like "OID.1.2.3.4", when + * no particular name for the algorithm is known. + */ + public String getName() + { return algName (); } + + /** + * Returns a string describing the algorithm and its parameters. + */ + public String toString() + { + return (algName() + paramsToString()); + } + + /** + * Returns the DER encoded parameter, which can then be + * used to initialize java.security.AlgorithmParamters. + * + * @return DER encoded parameters, or null not present. + */ + public byte[] getEncodedParams() throws IOException { + if (params == null) + return null; + else + return params.toByteArray(); + } + + /** + * Provides a human-readable description of the algorithm parameters. + * This may be redefined by subclasses which parse those parameters. + */ + protected String paramsToString() + { + if (params == null) { + return ""; + } else if (algParams != null) { + return algParams.toString(); + } else { + return ", params unparsed"; + } + } + + + /** + * Returns true iff the argument indicates the same algorithm + * with the same parameters. + */ + public boolean equals(AlgorithmId other) + { + if (!algid.equals (other.algid)) + return false; + else if (params == null && other.params == null) + return true; + else if (params == null) + return false; + else + return params.equals(other.params); + } + + + /** + * Compares this AlgorithmID to another. If algorithm parameters are + * available, they are compared. Otherwise, just the object IDs + * for the algorithm are compared. + * + * @param other preferably an AlgorithmId, else an ObjectIdentifier + */ + public boolean equals(Object other) + { + if (other instanceof AlgorithmId) + return equals((AlgorithmId) other); + else if (other instanceof ObjectIdentifier) + return equals((ObjectIdentifier) other); + else + return false; + } + + + /** + * Compares two algorithm IDs for equality. Returns true iff + * they are the same algorithm, ignoring algorithm parameters. + */ + public final boolean equals(ObjectIdentifier id) + { + return algid.equals(id); + } + + + /*****************************************************************/ + + /* + * HASHING ALGORITHMS + */ + private static final int MD2_data[] = { 1, 2, 840, 113549, 2, 2 }; + private static final int MD5_data[] = { 1, 2, 840, 113549, 2, 5 }; + // sha = { 1, 3, 14, 3, 2, 18 }; + private static final int SHA1_OIW_data[] = { 1, 3, 14, 3, 2, 26 }; + private static final int SHA256_data[] = {2, 16, 840, 1, 101, 3, 4, 2, 1}; + private static final int SHA512_data[] = {2, 16, 840, 1, 101, 3, 4, 2, 3}; + + /** + * Algorithm ID for the MD2 Message Digest Algorthm, from RFC 1319. + * OID = 1.2.840.113549.2.2 + */ + public static final ObjectIdentifier + MD2_oid = new ObjectIdentifier(MD2_data); + + /** + * Algorithm ID for the MD5 Message Digest Algorthm, from RFC 1321. + * OID = 1.2.840.113549.2.5 + */ + public static final ObjectIdentifier + MD5_oid = new ObjectIdentifier(MD5_data); + + /** + * Algorithm ID for the SHA1 Message Digest Algorithm, from FIPS 180-1. + * This is sometimes called "SHA", though that is often confusing since + * many people refer to FIPS 180 (which has an error) as defining SHA. + * OID = 1.3.14.3.2.26 + */ + public static final ObjectIdentifier + SHA_oid = new ObjectIdentifier(SHA1_OIW_data); + + public static final ObjectIdentifier + SHA256_oid = new ObjectIdentifier(SHA256_data); + + public static final ObjectIdentifier + SHA512_oid = new ObjectIdentifier(SHA512_data); + + /* + * COMMON PUBLIC KEY TYPES + */ + private static final int DH_data[] = { 1, 2, 840, 113549, 1, 3, 1 }; + private static final int DH_PKIX_data[] = { 1, 2, 840, 10046, 2, 1 }; + private static final int DSA_OIW_data[] = { 1, 3, 14, 3, 2, 12 }; + private static final int DSA_PKIX_data[] = { 1, 2, 840, 10040, 4, 1 }; + private static final int RSA_data[] = { 1, 2, 5, 8, 1, 1 }; + private static final int RSAEncryption_data[] = + { 1, 2, 840, 113549, 1, 1, 1 }; + private static final int ANSI_X962_public_key_data[] = + { 1, 2, 840, 10045, 2, 1 }; + private static final int ANSI_X962_sha1_with_ec_data[] = + { 1, 2, 840, 10045, 4, 1 }; + + public static final ObjectIdentifier + ANSIX962_EC_Public_Key_oid = new ObjectIdentifier(ANSI_X962_public_key_data); + public static final ObjectIdentifier + ANSIX962_SHA1_With_EC_oid = new ObjectIdentifier(ANSI_X962_sha1_with_ec_data); + + /* + * Note the preferred OIDs are named simply with no "OIW" or + * "PKIX" in them, even though they may point to data from these + * specs; e.g. SHA_oid, DH_oid, DSA_oid, SHA1WithDSA_oid... + */ + /** + * Algorithm ID for Diffie Hellman Key agreement, from PKCS #3. + * Parameters include public values P and G, and may optionally specify + * the length of the private key X. Alternatively, algorithm parameters + * may be derived from another source such as a Certificate Authority's + * certificate. + * OID = 1.2.840.113549.1.3.1 + */ + public static final ObjectIdentifier + DH_oid = new ObjectIdentifier(DH_data); + + /** + * Algorithm ID for the Diffie Hellman Key Agreement (DH), from the + * IETF PKIX IPKI Part I. + * Parameters may include public values P and G. + * OID = 1.2.840.10046.2.1 + */ + public static final ObjectIdentifier + DH_PKIX_oid = new ObjectIdentifier(DH_PKIX_data); + + /** + * Algorithm ID for the Digital Signing Algorithm (DSA), from the + * NIST OIW Stable Agreements part 12. + * Parameters may include public values P, Q, and G; or these may be + * derived from + * another source such as a Certificate Authority's certificate. + * OID = 1.3.14.3.2.12 + */ + public static final ObjectIdentifier + DSA_OIW_oid = new ObjectIdentifier(DSA_OIW_data); + + /** + * Algorithm ID for the Digital Signing Algorithm (DSA), from the + * IETF PKIX IPKI Part I. + * Parameters may include public values P, Q, and G; or these may be + * derived from + * another source such as a Certificate Authority's certificate. + * OID = 1.2.840.10040.4.1 + */ + public static final ObjectIdentifier + DSA_oid = new ObjectIdentifier(DSA_PKIX_data); + + /** + * Algorithm ID for RSA keys used for any purpose, as defined in X.509. + * The algorithm parameter is a single value, the number of bits in the + * public modulus. + * OID = 1.2.5.8.1.1 + */ + public static final ObjectIdentifier + RSA_oid = new ObjectIdentifier(RSA_data); + + + /** + * Algorithm ID for RSA keys used with RSA encryption, as defined + * in PKCS #1. There are no parameters associated with this algorithm. + * OID = 1.2.840.113549.1.1.1 + */ + public static final ObjectIdentifier + RSAEncryption_oid = new ObjectIdentifier(RSAEncryption_data); + + + /* + * COMMON SIGNATURE ALGORITHMS + */ + private static final int sha1WithEC_data[] = + { 1, 2, 840, 10045, 4, 1 }; + private static final int md2WithRSAEncryption_data[] = + { 1, 2, 840, 113549, 1, 1, 2 }; + private static final int md5WithRSAEncryption_data[] = + { 1, 2, 840, 113549, 1, 1, 4 }; + private static final int sha1WithRSAEncryption_data[] = + { 1, 2, 840, 113549 ,1, 1, 5 }; + private static final int sha256WithRSAEncryption_data[] = + { 1, 2, 840, 113549 ,1, 1, 11 }; + private static final int sha512WithRSAEncryption_data[] = + { 1, 2, 840, 113549 ,1, 1, 13 }; + private static final int sha1WithRSAEncryption_OIW_data[] = + { 1, 3, 14, 3, 2, 29 }; + private static final int shaWithDSA_OIW_data[] = + { 1, 3, 14, 3, 2, 13 }; + private static final int sha1WithDSA_OIW_data[] = + { 1, 3, 14, 3, 2, 27 }; + private static final int dsaWithSHA1_PKIX_data[] = + { 1, 2, 840, 10040, 4, 3 }; + + public static final ObjectIdentifier + sha1WithEC_oid = new + ObjectIdentifier(sha1WithEC_data); + + /** + * Identifies a signing algorithm where an MD2 digest is encrypted + * using an RSA private key; defined in PKCS #1. Use of this + * signing algorithm is discouraged due to MD2 vulnerabilities. + * OID = 1.2.840.113549.1.1.2 + */ + public static final ObjectIdentifier + md2WithRSAEncryption_oid = new + ObjectIdentifier(md2WithRSAEncryption_data); + + /** + * Identifies a signing algorithm where an MD5 digest is + * encrypted using an RSA private key; defined in PKCS #1. + * OID = 1.2.840.113549.1.1.4 + */ + public static final ObjectIdentifier + md5WithRSAEncryption_oid = new + ObjectIdentifier(md5WithRSAEncryption_data); + + /** + * The proper one for sha1/rsa + */ + public static final ObjectIdentifier + sha1WithRSAEncryption_oid = new + ObjectIdentifier(sha1WithRSAEncryption_data); + + /** + * The proper one for sha256/rsa + */ + public static final ObjectIdentifier + sha256WithRSAEncryption_oid = new + ObjectIdentifier(sha256WithRSAEncryption_data); + + /** + * The proper one for sha512/rsa + */ + public static final ObjectIdentifier + sha512WithRSAEncryption_oid = new + ObjectIdentifier(sha512WithRSAEncryption_data); + + /** + * Identifies a signing algorithm where an SHA1 digest is + * encrypted using an RSA private key; defined in NIST OIW. + * OID = 1.3.14.3.2.29 + */ + public static final ObjectIdentifier + sha1WithRSAEncryption_OIW_oid = new + ObjectIdentifier(sha1WithRSAEncryption_OIW_data); + + /** + * Identifies the FIPS 186 "Digital Signature Standard" (DSS), where a + * SHA digest is signed using the Digital Signing Algorithm (DSA). + * This should not be used. + * OID = 1.3.14.3.2.13 + */ + public static final ObjectIdentifier + shaWithDSA_OIW_oid = new ObjectIdentifier(shaWithDSA_OIW_data); + + /** + * Identifies the FIPS 186 "Digital Signature Standard" (DSS), where a + * SHA1 digest is signed using the Digital Signing Algorithm (DSA). + * OID = 1.3.14.3.2.27 + */ + public static final ObjectIdentifier + sha1WithDSA_OIW_oid = new ObjectIdentifier(sha1WithDSA_OIW_data); + + /** + * Identifies the FIPS 186 "Digital Signature Standard" (DSS), where a + * SHA1 digest is signed using the Digital Signing Algorithm (DSA). + * OID = 1.2.840.10040.4.3 + */ + public static final ObjectIdentifier + sha1WithDSA_oid = new ObjectIdentifier(dsaWithSHA1_PKIX_data); + + /** + * Supported signing algorithms for a DSA key. + */ + public static final String[] DSA_SIGNING_ALGORITHMS = new String[] + { "SHA1withDSA" }; + + /** + * Supported signing algorithms for a RSA key. + */ + public static final String[] RSA_SIGNING_ALGORITHMS = new String[] + { "SHA1withRSA", "SHA256withRSA", "SHA512withRSA", "MD5withRSA", "MD2withRSA" }; + + public static final String[] EC_SIGNING_ALGORITHMS = new String[] + { "SHA1withEC" }; + + /** + * All supported signing algorithms. + */ + public static final String[] ALL_SIGNING_ALGORITHMS = new String[] + { "SHA1withRSA", "MD5withRSA", "MD2withRSA", "SHA1withDSA", "SHA256withRSA", "SHA512withRSA", "SHA1withEC" }; + +} diff --git a/pki/base/util/src/netscape/security/x509/Attribute.java b/pki/base/util/src/netscape/security/x509/Attribute.java new file mode 100644 index 00000000..a5fa0224 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/Attribute.java @@ -0,0 +1,335 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.util.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.StringReader; +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; +import sun.io.ByteToCharConverter; + +import java.io.*; +import netscape.security.util.*; + +/** + * An attribute, as identified by some attribute ID, has some particular values. + * Values are as a rule ASN.1 printable strings. A conventional set of type IDs + * is recognized when parsing. The following shows the syntax: + * <pre> + * + * Attribute ::= SEQUENCE { + * type AttributeType, + * value SET OF AttributeValue + * -- at least one value is required --} + * + * AttributeType ::= OBJECT IDENTIFIER + * + * AttributeValue ::= ANY + * + * </pre> + * Refer to draft-ietf-pkix-ipki-part1-11 for the support attributes listed on + * page 96 of the internet draft. The are listed here for easy reference: name, + * common name, surname, given name, initials, generation qualifier, dn qualifier, + * country name, locality name, state or province name, organization name, organization + * unit name, title, pkcs9 email. Not all the attributes are supported. Please check + * the X500NameAttrMap for defined attributes. + * + * @author Christine Ho + */ + +public final class Attribute implements Serializable, DerEncoder { + + //private variables + ObjectIdentifier oid; + Vector valueSet = new Vector(); + transient protected X500NameAttrMap attrMap; + + //========== CONSTRUCTOR ================================== + + /** + * Construct an attribute from attribute type and attribute value + * @param oid the object identifier of the attribute type + * @param value the value string + */ + public Attribute (ObjectIdentifier oid, String value) + throws IOException + { + + //pre-condition verification + if ((oid == null) || (value == null)) + throw new IOException("Invalid Input - null passed"); + + attrMap = X500NameAttrMap.getDefault(); + this.oid = oid; + valueSet.addElement(value); + } + + /** + * Construct an attribute from attribute type and attribute values + * @param oid the object identifier of the attribute type + * @param values String value vector + */ + public Attribute (ObjectIdentifier oid, Vector values) + throws IOException + { + + //pre-condition verification + if ((oid == null) || (values == null)) + throw new IOException("Invalid Input - null passed"); + + attrMap = X500NameAttrMap.getDefault(); + this.oid = oid; + + //copy the value into the valueSet list + Enumeration vals = values.elements(); + while (vals.hasMoreElements()) { + Object obj = vals.nextElement(); + if (obj instanceof String) + valueSet.addElement(obj); + else + throw new IOException("values vectore must consist of String object"); + } + } + + /** + * Construct an attribute from attribute type and attribute values + * @param oid attribute type string CN,OU,O,C,L,TITLE,ST,STREET,UID,MAIL,E,DC + * @param values String value vector + */ + public Attribute (String attr, Vector values) + throws IOException + { + + //pre-condition verification + if ((attr == null) || (values == null)) + throw new IOException("Invalid Input - null passed"); + + ObjectIdentifier identifier = null; + try { + identifier = new ObjectIdentifier(attr); + } catch (Exception e) { + } + + ObjectIdentifier id = identifier; + if (identifier == null) { + attrMap = X500NameAttrMap.getDefault(); + id = attrMap.getOid(attr); + if (id == null) + throw new IOException("Attr is not supported - does not contain in attr map"); + } + this.oid = id; + + //copy the value into the valueSet list + Enumeration vals = values.elements(); + while (vals.hasMoreElements()) { + Object obj = vals.nextElement(); + if (obj instanceof String) + valueSet.addElement(obj); + else + throw new IOException("Values vectore must consist of String object"); + } + } + + /** + * Construct an attribute from a der encoded object. This der + * der encoded value should represent the attribute object. + * @param value the attribute object in der encode form. + */ + public Attribute (DerValue val) + throws IOException + { + + //pre-condition verification + if (val == null) + throw new IOException("Invalid Input - null passed"); + + attrMap = X500NameAttrMap.getDefault(); + + decodeThis(val); + + } + + //========== PUBLIC METHODS ================================== + + /** + * Returns the OID in the Attribute. + * @return the ObjectIdentifier in this Attribute. + */ + public ObjectIdentifier getOid() + { + return oid; + } + + /** + * Returns enumeration of values in this attribute. + * @return Enumeration of values of this Attribute. + */ + public Enumeration getValues() + { + if (valueSet == null) return null; + return valueSet.elements(); + } + + /** + * Encodes the Attribute to a Der output stream. + * Attribute are encoded as a SEQUENCE of two elements. + * @param out The Der output stream. + */ + public void encode(DerOutputStream out) throws IOException + { + encodeThis(out); + } + + /** + * DER encode this object onto an output stream. + * Implements the <code>DerEncoder</code> interface. + * + * @param out + * the output stream on which to write the DER encoding. + * + * @exception IOException on encoding error. + */ + public void derEncode (OutputStream out) throws IOException + { + encodeThis(out); + } + + /** + * Prints a string version of this extension. + */ + public String toString() + { + String theoid = "Attribute: "+oid+"\n"; + String values = "Values: "; + Enumeration n = valueSet.elements(); + if (n.hasMoreElements()) { + values += (String)n.nextElement(); + while (n.hasMoreElements()) + values+= ","+(String)n.nextElement(); + } + return theoid+values+"\n"; + } + + + //========== PRIVATE METHODS ================================== + + //encode the attribute object + private void encodeThis(OutputStream out) + throws IOException + { + DerOutputStream tmp = new DerOutputStream (); + DerOutputStream tmp2 = new DerOutputStream (); + + tmp.putOID (oid); + encodeValueSet(tmp); + tmp2.write (DerValue.tag_Sequence, tmp); + out.write(tmp2.toByteArray()); + } + + //encode the attribute object + private void encodeValueSet(OutputStream out) + throws IOException + { + DerOutputStream tmp = new DerOutputStream (); + DerOutputStream tmp2 = new DerOutputStream (); + + //get the attribute converter + AVAValueConverter converter = attrMap.getValueConverter(oid); + if (converter == null) { + converter = new GenericValueConverter(); + //throw new IOException("Converter not found: unsupported attribute type"); + } + + //loop through all the values and encode + Enumeration vals = valueSet.elements(); + while (vals.hasMoreElements()) { + String val = (String) vals.nextElement(); + DerValue derobj = converter.getValue(val); + derobj.encode(tmp); + } + + tmp2.write (DerValue.tag_SetOf, tmp); + out.write(tmp2.toByteArray()); + } + + //decode the attribute object + private void decodeThis(DerValue val) + throws IOException + { + + //pre-condition verification + if (val == null) { + throw new IOException("Invalid Input - null passed."); + } + + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for Attribute."); + } + + if (val.data.available() == 0) { + throw new IOException("No data available in " + + "passed DER encoded value."); + } + this.oid = val.data.getDerValue().getOID(); + + if (val.data.available() == 0) { + throw new IOException("Invalid encoding for Attribute - value missing"); + } + decodeValueSet(val.data.getDerValue()); + + if (this.oid == null) + throw new IOException("Invalid encoding for Attribute - OID missing"); + + } + + //decode the attribute value set + private void decodeValueSet(DerValue val) + throws IOException + { + //pre-condition verification + if (val == null) { + throw new IOException("Invalid Input - null passed."); + } + + AVAValueConverter converter = attrMap.getValueConverter(this.oid); + if (converter == null) { + converter = new GenericValueConverter(); + //throw new IOException("Attribute is not supported - not in attr map"); + } + + if (val.tag != DerValue.tag_SetOf) { + throw new IOException("Invalid encoding for Attribute Value Set."); + } + + if (val.data.available() == 0) { + throw new IOException("No data available in " + + "passed DER encoded attribute value set."); + } + + //get the value set + while (val.data.available() != 0) { + DerValue value = val.data.getDerValue(); + valueSet.addElement(converter.getAsString(value)); + } + } + +} + diff --git a/pki/base/util/src/netscape/security/x509/AttributeNameEnumeration.java b/pki/base/util/src/netscape/security/x509/AttributeNameEnumeration.java new file mode 100644 index 00000000..f8546d52 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/AttributeNameEnumeration.java @@ -0,0 +1,39 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.util.Vector; +import java.util.Enumeration; + +/** + * <p>This class provides the Enumeration implementation used + * by all the X509 certificate attributes to return the attribute + * names contained within them. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.3 + */ +public class AttributeNameEnumeration extends Vector { + /** + * The default constructor for this class. + */ + public AttributeNameEnumeration() { + super(1,1); + } +} diff --git a/pki/base/util/src/netscape/security/x509/AuthorityKeyIdentifierExtension.java b/pki/base/util/src/netscape/security/x509/AuthorityKeyIdentifierExtension.java new file mode 100644 index 00000000..9b92c45c --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/AuthorityKeyIdentifierExtension.java @@ -0,0 +1,333 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.util.Enumeration; + +import netscape.security.util.*; + +/** + * This class represents the Authority Key Identifier Extension. + * + * <p>The authority key identifier extension provides a means of + * identifying the particular public key used to sign a certificate. + * This extension would be used where an issuer has multiple signing + * keys (either due to multiple concurrent key pairs or due to + * changeover). + * <p> + * The ASN.1 syntax for this is: + * <pre> + * AuthorityKeyIdentifier ::= SEQUENCE { + * keyIdentifier [0] KeyIdentifier OPTIONAL, + * authorityCertIssuer [1] GeneralNames OPTIONAL, + * authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL + * } + * KeyIdentifier ::= OCTET STRING + * </pre> + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.9 + * @see Extension + * @see CertAttrSet + */ +public class AuthorityKeyIdentifierExtension extends Extension +implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = + "x509.info.extensions.AuthorityKeyIdentifier"; + /** + * Attribute names. + */ + public static final String NAME = "AuthorityKeyIdentifier"; + public static final String KEY_ID = "key_id"; + public static final String AUTH_NAME = "auth_name"; + public static final String SERIAL_NUMBER = "serial_number"; + + // Private data members + private static final byte TAG_ID = 0; + private static final byte TAG_NAMES = 1; + private static final byte TAG_SERIAL_NUM = 2; + + private KeyIdentifier id = null; + private GeneralNames names = null; + private SerialNumber serialNum = null; + + // Encode only the extension value + private void encodeThis() throws IOException { + DerOutputStream seq = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + if (id != null) { + DerOutputStream tmp1 = new DerOutputStream(); + id.encode(tmp1); + tmp.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + false, TAG_ID), tmp1); + } + try { + if (names != null) { + DerOutputStream tmp1 = new DerOutputStream(); + names.encode(tmp1); + tmp.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + true, TAG_NAMES), tmp1); + } + } catch (Exception e) { + throw new IOException(e.toString()); + } + if (serialNum != null) { + DerOutputStream tmp1 = new DerOutputStream(); + serialNum.encode(tmp1); + tmp.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + false, TAG_SERIAL_NUM), tmp1); + } + seq.write(DerValue.tag_Sequence, tmp); + this.extensionValue = seq.toByteArray(); + } + + /** + * Exposed critical parameter. 99/11/03 + */ + public AuthorityKeyIdentifierExtension(boolean critical, + KeyIdentifier kid, GeneralNames name, + SerialNumber sn) + throws IOException { + this.id = kid; + this.names = name; + this.serialNum = sn; + + this.extensionId = PKIXExtensions.AuthorityKey_Id; + this.critical = critical; + encodeThis(); + } + + /** + * The default constructor for this extension. Null parameters make + * the element optional (not present). + * + * @param id the KeyIdentifier associated with this extension. + * @param names the GeneralNames associated with this extension + * @param serialNum the CertificateSerialNumber associated with + * this extension. + * @exception IOException on error. + */ + public AuthorityKeyIdentifierExtension(KeyIdentifier kid, GeneralNames name, + SerialNumber sn) + throws IOException { + this.id = kid; + this.names = name; + this.serialNum = sn; + + this.extensionId = PKIXExtensions.AuthorityKey_Id; + this.critical = false; + encodeThis(); + } + + /** + * Create the extension from the passed DER encoded value of the same. + * + * @param critical true if the extension is to be treated as critical. + * @param value Array of DER encoded bytes of the actual value. + * @exception IOException on error. + */ + public AuthorityKeyIdentifierExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.AuthorityKey_Id; + this.critical = critical.booleanValue(); + + if (!(value instanceof byte[])) + throw new IOException("Illegal argument type"); + + int len = Array.getLength(value); + byte[] extValue = new byte[len]; + System.arraycopy(value, 0, extValue, 0, len); + + this.extensionValue = extValue; + DerValue val = new DerValue(extValue); + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for " + + "AuthorityKeyIdentifierExtension."); + } + + // NB. this is always encoded with the IMPLICIT tag + // The checks only make sense if we assume implicit tagging, + // with explicit tagging the form is always constructed. + while (val.data.available() != 0) { + DerValue opt = val.data.getDerValue(); + + if (opt.isContextSpecific(TAG_ID) && !opt.isConstructed()) { + if (id != null) + throw new IOException("Duplicate KeyIdentifier in " + + "AuthorityKeyIdentifier."); + opt.resetTag(DerValue.tag_OctetString); + id = new KeyIdentifier(opt); + + } else if (opt.isContextSpecific(TAG_NAMES) && + opt.isConstructed()) { + if (names != null) + throw new IOException("Duplicate GeneralNames in " + + "AuthorityKeyIdentifier."); + try { + opt.resetTag(DerValue.tag_Sequence); + names = new GeneralNames(opt); + } catch (GeneralNamesException e) { + throw new IOException(e.toString()); + } + + } else if (opt.isContextSpecific(TAG_SERIAL_NUM) && + !opt.isConstructed()) { + if (serialNum != null) + throw new IOException("Duplicate SerialNumber in " + + "AuthorityKeyIdentifier."); + opt.resetTag(DerValue.tag_Integer); + serialNum = new SerialNumber(opt); + } else + throw new IOException("Invalid encoding of " + + "AuthorityKeyIdentifierExtension."); + } + } + + /** + * Return the object as a string. + */ + public String toString() { + String s = super.toString() + "AuthorityKeyIdentifier [\n"; + if (id != null) { + s += id.toString(); + } + if (names != null) { + s += names.toString() + "\n"; + } + if (serialNum != null) { + s += serialNum.toString() + "\n"; + } + return (s + "]\n"); + } + + /** + * Decode the extension from the InputStream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception IOException on decoding or validity errors. + */ + public void decode(InputStream in) throws IOException { + throw new IOException("Method not to be called directly."); + } + + /** + * Write the extension to the OutputStream. + * + * @param out the OutputStream to write the extension to. + * @exception IOException on error. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (this.extensionValue == null) { + extensionId = PKIXExtensions.AuthorityKey_Id; + critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + clearValue(); + if (name.equalsIgnoreCase(KEY_ID)) { + if (!(obj instanceof KeyIdentifier)) { + throw new IOException("Attribute value should be of " + + "type KeyIdentifier."); + } + id = (KeyIdentifier)obj; + } else if (name.equalsIgnoreCase(AUTH_NAME)) { + if (!(obj instanceof GeneralNames)) { + throw new IOException("Attribute value should be of " + + "type GeneralNames."); + } + names = (GeneralNames)obj; + } else if (name.equalsIgnoreCase(SERIAL_NUMBER)) { + if (!(obj instanceof SerialNumber)) { + throw new IOException("Attribute value should be of " + + "type SerialNumber."); + } + serialNum = (SerialNumber)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:AuthorityKeyIdentifier."); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(KEY_ID)) { + return (id); + } else if (name.equalsIgnoreCase(AUTH_NAME)) { + return (names); + } else if (name.equalsIgnoreCase(SERIAL_NUMBER)) { + return (serialNum); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:AuthorityKeyIdentifier."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(KEY_ID)) { + id = null; + } else if (name.equalsIgnoreCase(AUTH_NAME)) { + names = null; + } else if (name.equalsIgnoreCase(SERIAL_NUMBER)) { + serialNum = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:AuthorityKeyIdentifier."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements () { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(KEY_ID); + elements.addElement(AUTH_NAME); + elements.addElement(SERIAL_NUMBER); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName () { + return (NAME); + } +} diff --git a/pki/base/util/src/netscape/security/x509/BasicConstraintsExtension.java b/pki/base/util/src/netscape/security/x509/BasicConstraintsExtension.java new file mode 100644 index 00000000..925e12fa --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/BasicConstraintsExtension.java @@ -0,0 +1,287 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.util.Enumeration; + +import netscape.security.util.*; + +/** + * This class represents the Basic Constraints Extension. + * + * <p>The basic constraints extension identifies whether the subject of the + * certificate is a CA and how deep a certification path may exist + * through that CA. + * + * <pre> + * The ASN.1 syntax for this extension is: + * BasicConstraints ::= SEQUENCE { + * cA BOOLEAN DEFAULT FALSE, + * pathLenConstraint INTEGER (0..MAX) OPTIONAL + * } + * </pre> + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.7 + * @see CertAttrSet + * @see Extension + */ +public class BasicConstraintsExtension extends Extension +implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.extensions.BasicConstraints"; + /** + * Attribute names. + */ + public static final String NAME = "BasicConstraints"; + public static final String IS_CA = "is_ca"; + public static final String PATH_LEN = "path_len"; + + // Private data members + private boolean ca = false; + private int pathLen = -1; + + // Encode this extension value + private void encodeThis() throws IOException { + DerOutputStream out = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + + if (ca) { + tmp.putBoolean(ca); + } + if (pathLen >= 0) { + tmp.putInteger(new BigInt(pathLen)); + } + out.write(DerValue.tag_Sequence, tmp); + this.extensionValue = out.toByteArray(); + } + + /** + * Default constructor for this object. + * + * @param ca true, if the subject of the Certificate is a CA. + * @param len specifies the depth of the certification path. + */ + public BasicConstraintsExtension(boolean ca, int len) throws IOException { + this.ca = ca; + this.pathLen = len; + this.extensionId = PKIXExtensions.BasicConstraints_Id; + if (ca) { + critical = true; + } else { + critical = false; + } + encodeThis(); + } + + /** + * Default constructor for this object. + * + * @param ca true, if the subject of the Certificate is a CA. + * @param len specifies the depth of the certification path. + */ + public BasicConstraintsExtension(boolean ca, boolean critical, int len) throws IOException { + this.ca = ca; + this.pathLen = len; + this.extensionId = PKIXExtensions.BasicConstraints_Id; + this.critical = critical; + encodeThis(); + } + + /** + * Create the extension from the passed DER encoded value of the same. + * + * @param extension the DER encoded value of the extension. + * @exception IOException on error. + */ + public BasicConstraintsExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.BasicConstraints_Id; + this.critical = critical.booleanValue(); + + if (value instanceof byte[]) { + int len = Array.getLength(value); + byte[] extValue = new byte[len]; + System.arraycopy(value, 0, extValue, 0, len); + + this.extensionValue = extValue; + DerValue val = new DerValue(extValue); + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding of BasicConstraints"); + } + + // non-CA cert with no limit to certification path length + if (val.data == null || val.data.available() < 1) { + this.ca = false; + this.pathLen = -1; + return; + } + DerValue opt = val.data.getDerValue(); + if (opt.tag != DerValue.tag_Boolean) { + this.ca = false; + } else { + this.ca = true; + if (val.data.available() != 0) { + opt = val.data.getDerValue(); + } else { + this.pathLen = -1; + return; + } + } + if (opt.tag != DerValue.tag_Integer) { + throw new IOException("Invalid encoding of BasicConstraints"); + } + this.pathLen = (opt.getInteger()).toInt(); + /* + * Activate this check once again after PKIX profiling + * is a standard and this check no longer imposes an + * interoperability barrier. + * if (ca) { + * if (!this.critical) { + * throw new IOException("Criticality cannot be false for CA."); + * } + * } + */ + } else + throw new IOException("Invalid argument type"); + } + + /** + * Return user readable form of extension. + */ + public String toString() { + String s = super.toString() + "BasicConstraints:[\n"; + + s += ((ca) ? ("CA:true") : ("CA:false")) + "\n"; + if (pathLen >= 0) { + s += "PathLen:" + pathLen + "\n"; + } else { + s += "PathLen: undefined\n"; + } + return (s + "]\n"); + } + + /** + * Decode the extension from the InputStream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception IOException on decoding or validity errors. + */ + public void decode(InputStream in) throws IOException { + throw new IOException("Method not to be called directly."); + } + + /** + * Encode this extension value to the output stream. + * + * @param out the DerOutputStream to encode the extension to. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (extensionValue == null) { + this.extensionId = PKIXExtensions.BasicConstraints_Id; +/* #57286 - so that profile can set critiality */ +/* + if (ca) { + critical = true; + } else { + critical = false; + } +*/ + encodeThis(); + } + super.encode(tmp); + + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + clearValue(); + if (name.equalsIgnoreCase(IS_CA)) { + if (!(obj instanceof Boolean)) { + throw new IOException("Attribute value should be of type Boolean."); + } + ca = ((Boolean)obj).booleanValue(); + } else if (name.equalsIgnoreCase(PATH_LEN)) { + if (!(obj instanceof Integer)) { + throw new IOException("Attribute value should be of type Integer."); + } + pathLen = ((Integer)obj).intValue(); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:BasicConstraints."); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(IS_CA)) { + return (new Boolean(ca)); + } else if (name.equalsIgnoreCase(PATH_LEN)) { + return (Integer.valueOf(pathLen)); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:BasicConstraints."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(IS_CA)) { + ca = false; + } else if (name.equalsIgnoreCase(PATH_LEN)) { + pathLen = -1; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:BasicConstraints."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(IS_CA); + elements.addElement(PATH_LEN); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/pki/base/util/src/netscape/security/x509/CPSuri.java b/pki/base/util/src/netscape/security/x509/CPSuri.java new file mode 100644 index 00000000..e7094eab --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CPSuri.java @@ -0,0 +1,61 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import netscape.security.util.*; + + +/** + * Represent the CPSuri Qualifier. + * + * CPSuri ::= IA5String; + * + * @author Thomas Kwan + */ +public class CPSuri extends Qualifier { + + private String mURI = null; + + /** + * Create a PolicyQualifierInfo + * + * @param id the ObjectIdentifier for the policy id. + */ + public CPSuri(String uri) { + mURI = uri; + } + + public CPSuri(DerValue val) throws IOException { + mURI = val.getIA5String(); + } + + /** + * Write the PolicyQualifier to the DerOutputStream. + * + * @param out the DerOutputStream to write the object to. + * @exception IOException on errors. + */ + public void encode(DerOutputStream out) throws IOException { + out.putIA5String(mURI); + } + + public String getURI() { + return mURI; + } +} diff --git a/pki/base/util/src/netscape/security/x509/CRLDistributionPoint.java b/pki/base/util/src/netscape/security/x509/CRLDistributionPoint.java new file mode 100644 index 00000000..6cbca4ad --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CRLDistributionPoint.java @@ -0,0 +1,473 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import netscape.security.util.*; +import netscape.security.x509.*; +import org.mozilla.jss.asn1.*; +import java.io.*; + +/** + * <pre> + * DistributionPoint ::= SEQUENCE { + * distributionPoint [0] DistributionPointName OPTIONAL, + * reasons [1] ReasonFlags OPTIONAL, + * cRLIssuer [2] GeneralNames OPTIONAL } + * + * DistributionPointName ::= CHOICE { + * fullName [0] GeneralNames, + * nameRelativeToCRLIssuer [1] RelativeDistinguishedName } + * + * ReasonFlags ::= BIT STRING { + * unused (0), + * keyCompromise (1), + * cACompromise (2), + * affiliationChanged (3), + * superseded (4), + * cessationOfOperation (5), + * certificateHold (6) } + * </pre> + */ +public class CRLDistributionPoint implements ASN1Value { + + // at most one of the two following may be specified: + private GeneralNames fullName; + private RDN relativeName; + + // cache encoding of fullName + private ANY fullNameEncoding; + + private BitArray reasons; // optional, may be null + private GeneralNames CRLIssuer; // optional, may be null + private ANY CRLIssuerEncoding; + + // default constructor does nothing. + + /** + * Returns the <code>fullName</code> of the + * <code>DistributionPointName</code>, which may be <code>null</code>. + */ + public GeneralNames getFullName() { + return fullName; + } + + /** + * Returns the <code>relativeName</code> of the + * <code>DistributionPointName</code>, which may be <code>null</code>. + */ + public RDN getRelativeName() { + return relativeName; + } + + /** + * Sets the <code>fullName</code> of the + * <code>DistributionPointName</code>. It may be set to <code>null</code>. + * If it is set to a non-null value, <code>relativeName</code> will be + * set to <code>null</code>, because at most one of these two attributes + * can be specified at a time. + * @exception GeneralNamesException If an error occurs encoding the + * name. + */ + public void setFullName(GeneralNames fullName) + throws GeneralNamesException, IOException + { + this.fullName = fullName; + if( fullName != null ) { + // encode the name to catch any problems with it + DerOutputStream derOut = new DerOutputStream(); + fullName.encode(derOut); + try { + ANY raw = new ANY(derOut.toByteArray()); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + raw.encodeWithAlternateTag( Tag.get(0), bos ); + fullNameEncoding = new ANY( bos.toByteArray() ); + } catch(InvalidBERException e) { + // assume this won't happen, since it would imply a bug + // in DerOutputStream + throw new GeneralNamesException( e.toString() ); + } + + this.relativeName = null; + } + } + + /** + * Sets the <code>relativeName</code> of the + * <code>DistributionPointName</code>. It may be set to <code>null</code>. + * If it is set to a non-null value, <code>fullName</code> will be + * set to <code>null</code>, because at most one of these two attributes + * can be specified at a time. + */ + public void setRelativeName(RDN relativeName) { + this.relativeName = relativeName; + if( relativeName != null ) { + this.fullName = null; + } + } + + /** + * Returns the reason flags for this distribution point. May be + * <code>null</code>. + */ + public BitArray getReasons() { + return reasons; + } + + /** + * Sets the reason flags for this distribution point. May be set to + * <code>null</code>. + */ + public void setReasons(BitArray reasons) { + this.reasons = reasons; + } + + + /** + * Returns the CRLIssuer for the CRL at this distribution point. + * May be <code>null</code>. + */ + public GeneralNames getCRLIssuer() { + return CRLIssuer; + } + + /** + * Sets the CRLIssuer for the CRL at this distribution point. + * May be set to <code>null</code>. + * @exception GeneralNamesException If an error occurs encoding the name. + */ + public void setCRLIssuer(GeneralNames CRLIssuer) + throws GeneralNamesException, IOException + { + this.CRLIssuer = CRLIssuer; + + if( CRLIssuer != null ) { + // encode the name to catch any problems with it + DerOutputStream derOut = new DerOutputStream(); + CRLIssuer.encode(derOut); + try { + ANY raw = new ANY( derOut.toByteArray() ); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + raw.encodeWithAlternateTag( Tag.get(2), bos); + CRLIssuerEncoding = new ANY(bos.toByteArray()); + } catch(InvalidBERException e) { + throw new GeneralNamesException(e.toString()); + } + } + } + + ///////////////////////////////////////////////////////////// + // DER encoding + ///////////////////////////////////////////////////////////// + + private static final Tag TAG = SEQUENCE.TAG; + + public Tag getTag() { + return TAG; + } + + public void encode(OutputStream ostream) throws IOException { + encode(TAG, ostream); + } + + public void encode(Tag implicitTag, OutputStream ostream) + throws IOException + { + SEQUENCE seq = new SEQUENCE(); + DerOutputStream derOut; + + try { + + // Encodes the DistributionPointName. Because DistributionPointName + // is a CHOICE, the [0] tag is forced to be EXPLICIT. + if( fullName != null ) { + EXPLICIT distPoint = new EXPLICIT( Tag.get(0), fullNameEncoding); + seq.addElement( distPoint ); + } else if( relativeName != null ) { + derOut = new DerOutputStream(); + relativeName.encode(derOut); + ANY rn = new ANY(derOut.toByteArray()); + EXPLICIT raw = new EXPLICIT( Tag.get(1), rn ); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + raw.encode( bos ); + ANY distPointName = new ANY(bos.toByteArray()); + EXPLICIT distPoint = new EXPLICIT( Tag.get(0), distPointName); + seq.addElement( distPoint ); + } + + // Encodes the ReasonFlags. + if( reasons != null ) { + derOut = new DerOutputStream(); + derOut.putUnalignedBitString(reasons); + ANY raw = new ANY(derOut.toByteArray()); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + raw.encodeWithAlternateTag(Tag.get(1), bos); + ANY reasonEncoding = new ANY(bos.toByteArray()); + seq.addElement( Tag.get(1), reasonEncoding); + } + + // Encodes the CRLIssuer + if( CRLIssuer != null ) { + seq.addElement( Tag.get(2), CRLIssuerEncoding ); + } + + seq.encode(implicitTag, ostream); + + } catch(InvalidBERException e) { + // this shouldn't happen unless there is a bug in one of + // the Sun encoding classes + throw new IOException(e.toString()); + } + } + + // Template singleton + private static Template templateInstance = new Template(); + + /** + * Returns an instance of a template for decoding a CRLDistributionPoint. + */ + public static Template getTemplate() { + return templateInstance; + } + + public static void main(String args[]) { + try { + if( args.length != 1 ) { + System.out.println("Usage: CRLDistributionPoint <outfile>"); + System.exit(-1); + } + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + SEQUENCE cdps = new SEQUENCE(); + + // URI only + CRLDistributionPoint cdp = new CRLDistributionPoint(); + URIName uri = new URIName("http://www.mycrl.com/go/here"); + GeneralNames generalNames = new GeneralNames(); + generalNames.addElement(uri); + cdp.setFullName(generalNames); + cdps.addElement(cdp); + + // DN only + cdp = new CRLDistributionPoint(); + X500Name dn = new X500Name("CN=Otis Smith,E=otis@fedoraproject.org"+ + ",OU=Certificate Server,O=Fedora,C=US"); + generalNames = new GeneralNames(); + generalNames.addElement(dn); + cdp.setFullName(generalNames); + cdps.addElement(cdp); + + // DN + reason + BitArray ba = new BitArray(5, new byte[] {(byte)0x28} ); + cdp = new CRLDistributionPoint(); + cdp.setFullName(generalNames); + cdp.setReasons(ba); + cdps.addElement(cdp); + + + // relative DN + reason + crlIssuer + cdp = new CRLDistributionPoint(); + RDN rdn = new RDN("OU=foobar dept"); + cdp.setRelativeName(rdn); + cdp.setReasons(ba); + cdp.setCRLIssuer(generalNames); + cdps.addElement(cdp); + + cdps.encode(bos); + + byte[] encoded = bos.toByteArray(); + (new FileOutputStream(args[0])).write(encoded); + + SEQUENCE.OF_Template seqt = new SEQUENCE.OF_Template(getTemplate()); + + cdps = (SEQUENCE) ASN1Util.decode(seqt, encoded); + + int size = cdps.size(); + System.out.println("Total number of CDPs: " + size); + for( int i = 0; i < size; i++) { + System.out.println("\nCDP " + i); + cdp = (CRLDistributionPoint) cdps.elementAt(i); + GeneralNames gn = cdp.getFullName(); + if( gn == null ) { + System.out.println("No full name"); + } else { + System.out.println(gn); + } + rdn = cdp.getRelativeName(); + if( rdn == null ) { + System.out.println("No relative name"); + } else { + System.out.println(rdn); + } + if( cdp.getReasons() == null ) { + System.out.println("No reasons"); + } else { + System.out.println(cdp.getReasons()); + } + gn = cdp.getCRLIssuer(); + if( gn == null ) { + System.out.println("No cRLIssuer"); + } else { + System.out.println(gn); + } + } + System.out.println("Done"); + + + } catch(Exception e) { + e.printStackTrace(); + } + } + + +/** + * Template for decoding CRLDistributionPoint. + */ +public static class Template implements ASN1Template { + + public boolean tagMatch(Tag tag) { + return TAG.equals(tag); + } + + public ASN1Value decode(InputStream istream) + throws IOException, InvalidBERException + { + return decode(TAG, istream); + } + + public ASN1Value decode(Tag implicitTag, InputStream istream) + throws IOException, InvalidBERException + { + CRLDistributionPoint cdp = new CRLDistributionPoint(); + + // + // construct the top-level sequence + // + + SEQUENCE.Template seqt = SEQUENCE.getTemplate(); + + // distributionPoint + seqt.addOptionalElement( + new EXPLICIT.Template(Tag.get(0), ANY.getTemplate()) ); + + // reasons + seqt.addOptionalElement( Tag.get(1), BIT_STRING.getTemplate()); + + // cRLIssuer + // This will have a tag of 2, but we can't say that here + // because ANYs can't have implicit tags. We don't need to say + // it, because we do check the tags on the other two elements + // in the sequence, so we'll know if we get this one. + seqt.addOptionalElement( ANY.getTemplate() ); + + // + // decode the top-level sequence + // + SEQUENCE top = (SEQUENCE) seqt.decode(implicitTag, istream); + + + // decode the distribution point name + if( top.elementAt(0) != null ) { + EXPLICIT exp = (EXPLICIT) top.elementAt(0); + ANY distPoint = (ANY) exp.getContent(); + if( distPoint.getTag().equals(Tag.get(0)) ) { + // fullName + try { + DerValue dv = new DerValue(distPoint.getEncoded()); + //toFile("encodedFullName", distPoint.getEncoded()); + dv.resetTag(dv.tag_Sequence); + cdp.setFullName( new GeneralNames(dv) ); + } catch(GeneralNamesException e) { + throw new InvalidBERException( "fullName: " + e.toString()); + } catch(IOException e) { + throw new InvalidBERException( "fullName: " + e.toString()); + } + } else if( distPoint.getTag().equals(Tag.get(1)) ) { + // relative name + try { + DerValue dv = new DerValue(distPoint.getEncoded()); + /* dv is as follows: + 0 12: [1] { + 2 10: SET { + 4 8: SEQUENCE { + 6 3: OBJECT IDENTIFIER commonName (2 5 4 3) + 11 1: PrintableString 'x' + : } + : } + : } + */ + dv = dv.data.getDerValue(); // skipping the tag + /* after the skipping, we have: + 0 10: SET { + 2 8: SEQUENCE { + 4 3: OBJECT IDENTIFIER commonName (2 5 4 3) + 9 1: PrintableString 'x' + : } + : } + */ + dv.resetTag(dv.tag_Set); + cdp.setRelativeName( new RDN(dv) ); + } catch(IOException e) { + throw new InvalidBERException( "relativeName " + + e.toString() ); + } + } else { + throw new InvalidBERException( + "Unknown tag " + distPoint.getTag() + + " in distributionPoint" ); + } + } + + // decode the reasons + if( top.elementAt(1) != null ) { + BIT_STRING bs = (BIT_STRING) top.elementAt(1); + byte[] bits = bs.getBits(); + cdp.setReasons( + new BitArray( (bits.length * 8) - bs.getPadCount(), bits) ); + } + + // decode the cRLIssuer + if( top.elementAt(2) != null ) { + ANY issuer = (ANY) top.elementAt(2); + if( ! issuer.getTag().equals(Tag.get(2)) ) { + throw new InvalidBERException("Invalid tag " + issuer.getTag()); + } + try { + DerValue dv = new DerValue( issuer.getEncoded() ); + dv.resetTag(dv.tag_Sequence); + cdp.setCRLIssuer( new GeneralNames(dv) ); + } catch(GeneralNamesException e) { + throw new InvalidBERException( "cRLIssuer " + e.toString() ); + } catch(IOException e) { + throw new InvalidBERException( "cRLIssuer " + e.toString() ); + } + } + + return cdp; + + } +} + +private static void toFile(String filename, byte[] bytes) throws IOException{ + FileOutputStream fos = new FileOutputStream(filename); + fos.write(bytes); + fos.close(); +} + + +} diff --git a/pki/base/util/src/netscape/security/x509/CRLDistributionPointsExtension.java b/pki/base/util/src/netscape/security/x509/CRLDistributionPointsExtension.java new file mode 100644 index 00000000..f0503814 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CRLDistributionPointsExtension.java @@ -0,0 +1,377 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import netscape.security.util.*; +import netscape.security.x509.*; +import java.io.*; +import org.mozilla.jss.asn1.*; +import java.security.cert.*; +import java.util.Vector; +import java.util.Enumeration; +import java.util.Hashtable; + +/** + * An extension that tells applications where to find the CRL for + * this certificate. + * + * <pre> + * cRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint + * + * DistributionPoint ::= SEQUENCE { + * distributionPoint [0] DistributionPointName OPTIONAL, + * reasons [1] ReasonFlags OPTIONAL, + * cRLIssuer [2] GeneralNames OPTIONAL } + * + * DistributionPointName ::= CHOICE { + * fullName [0] GeneralNames, + * nameRelativeToCRLIssuer [1] RelativeDistinguishedName } + * + * ReasonFlags ::= BIT STRING { + * unused (0), + * keyCompromise (1), + * cACompromise (2), + * affiliationChanged (3), + * superseded (4), + * cessationOfOperation (5), + * certificateHold (6) } + * </pre> + */ +public class CRLDistributionPointsExtension extends Extension + implements CertAttrSet +{ + + // vector of CRLDistributionPoint + private SEQUENCE distributionPoints = new SEQUENCE(); + + // no default constructor + private CRLDistributionPointsExtension() { } + + // Cached DER-encoding to improve performance. + private byte[] cachedEncoding=null; + + /** + * This constructor is called by the CertificateExtensions class to decode + * an extension whose OID indicates it is a CRLDistributionsPoints + * extension. + */ + public CRLDistributionPointsExtension(Boolean critical, Object value) + //throws IOException + { + try { + this.extensionId = PKIXExtensions.CRLDistributionPoints_Id; + this.critical = critical.booleanValue(); + this.extensionValue = (byte[])((byte[])value).clone(); + + // decode the value + try { + SEQUENCE.OF_Template seqOfCRLDP = + new SEQUENCE.OF_Template( CRLDistributionPoint.getTemplate() ); + + distributionPoints = + (SEQUENCE) ASN1Util.decode( seqOfCRLDP, extensionValue ); + } catch(InvalidBERException e) { + throw new IOException("Invalid BER-encoding: " + e.toString()); + } + } catch(IOException e) { + System.out.println("Big error"); + System.out.println(e); + e.printStackTrace(); + //throw e; + } + } + + /** + * The Object Identifier for this extension. + */ + public static final String OID = "2.5.29.31"; + + /** + * Creates a new CRLDistributionPoints extension, with the given + * distribution point as the first element. + */ + public CRLDistributionPointsExtension(CRLDistributionPoint dp) { + this.extensionId = PKIXExtensions.CRLDistributionPoints_Id; + this.critical = false; + distributionPoints.addElement(dp); + } + + /** + * Adds an additional distribution point to the end of the sequence. + */ + public void addPoint(CRLDistributionPoint dp) { + distributionPoints.addElement(dp); + cachedEncoding = null; + } + + /** + * Returns the number of distribution points in the sequence. + */ + public int getNumPoints() { + return distributionPoints.size(); + } + + /** + * Returns the DistributionPoint at the given index in the sequence. + */ + public CRLDistributionPoint getPointAt(int index) { + return (CRLDistributionPoint) distributionPoints.elementAt(index); + } + + /** + * Sets the criticality of this extension. PKIX dictates that this + * extension SHOULD NOT be critical, so applications can make it critical + * if they have a very good reason. By default, the extension is not + * critical. + */ + public void setCritical(boolean critical) { + this.critical = critical; + } + + /** + * Encodes this extension to the given DerOutputStream. + * This method re-encodes each time it is called, so it is not very + * efficient. + */ + public void encode(DerOutputStream out) throws IOException { + extensionValue = ASN1Util.encode(distributionPoints); + super.encode(out); + } + + /** + * Should be called if any change is made to this data structure + * so that the cached DER encoding can be discarded. + */ + public void flushCachedEncoding() { + cachedEncoding = null; + } + + ///////////////////////////////////////////////////////////// + // CertAttrSet interface + // This interface is not really appropriate for this extension + // because it is so complicated. Therefore, we only provide a + // minimal implementation. + ///////////////////////////////////////////////////////////// + public static final String NAME = "CRLDistributionPoints"; + + public String toString() { + return NAME; + } + + /** + * DER-encodes this extension to the given OutputStream. + */ + public void encode(OutputStream ostream) + throws CertificateException, IOException + { + if( cachedEncoding == null ) { + // only re-encode if necessary + DerOutputStream tmp = new DerOutputStream(); + encode(tmp); + cachedEncoding = tmp.toByteArray(); + } + ostream.write(cachedEncoding); + } + + public void decode(InputStream in) + throws CertificateException, IOException + { + throw new IOException("Not supported"); + } + + public void set(String name, Object obj) + throws CertificateException, IOException + { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CRLDistributionPointsExtension"); + } + + public Object get(String name) + throws CertificateException, IOException + { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CRLDistributionPointsExtension"); + } + + public void delete(String name) + throws CertificateException, IOException + { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CRLDistributionPointsExtension"); + } + + public Enumeration getElements() { + return (new Vector()).elements(); + } + + + public String getName() { + return NAME; + } + + /** + * Test driver. + */ + public static void main(String args[]) { + + try { + + if( args.length != 1 ) { + System.out.println("Usage: CRLDistributionPointsExtentions "+ + "<outfile>"); + System.exit(-1); + } + + BufferedOutputStream bos = new BufferedOutputStream( + new FileOutputStream(args[0]) ); + + + // URI only + CRLDistributionPoint cdp = new CRLDistributionPoint(); + URIName uri = new URIName("http://www.mycrl.com/go/here"); + GeneralNames generalNames = new GeneralNames(); + generalNames.addElement(uri); + cdp.setFullName(generalNames); + CRLDistributionPointsExtension crldpExt = + new CRLDistributionPointsExtension(cdp); + + // DN only + cdp = new CRLDistributionPoint(); + X500Name dn = new X500Name("CN=Otis Smith,E=otis@fedoraproject.org"+ + ",OU=Certificate Server,O=Fedora,C=US"); + generalNames = new GeneralNames(); + generalNames.addElement(dn); + cdp.setFullName(generalNames); + crldpExt.addPoint(cdp); + + // DN + reason + BitArray ba = new BitArray(5, new byte[] {(byte)0x28} ); + cdp = new CRLDistributionPoint(); + cdp.setFullName(generalNames); + cdp.setReasons(ba); + crldpExt.addPoint(cdp); + + + // relative DN + reason + crlIssuer + cdp = new CRLDistributionPoint(); + RDN rdn = new RDN("OU=foobar dept"); + cdp.setRelativeName(rdn); + cdp.setReasons(ba); + cdp.setCRLIssuer(generalNames); + crldpExt.addPoint(cdp); + + crldpExt.setCritical(true); + crldpExt.encode(bos); + + bos.close(); + + } catch(Exception e) { + e.printStackTrace(); + } + } + + /** + * Represents a reason that a cert may be revoked. These reasons are + * expressed in a ReasonFlags bit string. + */ + public static class Reason { + + private String name; + private byte bitMask; + + private Reason() { } + private Reason(String name, byte bitMask) { + this.name = name; + this.bitMask = bitMask; + map.put(name, this); + list.addElement(this); + } + + private static Hashtable map = new Hashtable(); + private static Vector list = new Vector(); + + public static Reason fromString(String name) { + return (Reason) map.get(name); + } + + public String getName() { + return name; + } + + public byte getBitMask() { + return bitMask; + } + + /** + * Given a bit array representing reason flags, extracts the reasons + * and returns them as an array. + * + * @param bitFlags A bit vector containing reason flags. + * @return An array of reasons contained in the bit vector. + * May be zero-length but will not be null. + */ + public static Reason[] bitArrayToReasonArray(byte bitFlags) { + return bitArrayToReasonArray( new byte[] { bitFlags } ); + } + + /** + * Given a bit array representing reason flags, extracts the reasons + * and returns them as an array. Currently, only the first byte + * of the bitflags are examined. + * + * @param bitFlags A bit vector containing reason flags. The format + * is big-endian (MSB first). Only the first byte is examined. + * @return An array of reasons contained in the bit vector. + * May be zero-length but will not be null. + */ + public static Reason[] bitArrayToReasonArray(byte[] bitFlags) { + byte first = bitFlags[0]; + int size = list.size(); + Vector result = new Vector(); + for(int i = 0; i < size; i++) { + Reason r = (Reason) list.elementAt(i); + byte b = r.getBitMask(); + if( (first & b) != 0 ) { + result.addElement(r); + } + } + size = result.size(); + Reason[] retval = new Reason[size]; + for(int i=0; i < size; i++) { + retval[i] = (Reason) result.elementAt(i); + } + return retval; + } + + public static final Reason UNUSED = + new Reason("unused", (byte) 0x80); + public static final Reason KEY_COMPROMISE = + new Reason("keyCompromise", (byte) 0x40); + public static final Reason CA_COMPROMISE = + new Reason("cACompromise", (byte) 0x20); + public static final Reason AFFILIATION_CHANGED = + new Reason("affiliationChanged", (byte) 0x10); + public static final Reason SUPERSEDED = + new Reason("superseded", (byte) 0x08); + public static final Reason CESSATION_OF_OPERATION = + new Reason("cessationOfOperation", (byte) 0x04); + public static final Reason CERTIFICATE_HOLD = + new Reason("certificateHold", (byte) 0x02); + } + +} diff --git a/pki/base/util/src/netscape/security/x509/CRLExtensions.java b/pki/base/util/src/netscape/security/x509/CRLExtensions.java new file mode 100755 index 00000000..82236136 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CRLExtensions.java @@ -0,0 +1,222 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.security.cert.CRLException; +import java.security.cert.CertificateException; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import netscape.security.util.*; + +/** + * This class defines the CRL Extensions. + * + * @author Hemma Prafullchandra + * @version 1.4 + */ +public class CRLExtensions extends Vector { + + private Hashtable map; + + // Parse the encoded extension + private void parseExtension(Extension ext) throws X509ExtensionException { + try { + Class extClass = OIDMap.getClass(ext.getExtensionId()); + if (extClass == null) { // Unsupported extension + if (ext.isCritical()) { + throw new IOException("Unsupported CRITICAL extension: " + + ext.getExtensionId()); + } else { + map.put(ext.getExtensionId().toString(), ext); + addElement(ext); + return; + } + } + Class[] params = { Boolean.class, Object.class }; + Constructor cons = extClass.getConstructor(params); + byte[] extData = ext.getExtensionValue(); + int extLen = extData.length; + Object value = Array.newInstance(byte.class, extLen); + + for (int i = 0; i < extLen; i++) { + Array.setByte(value, i, extData[i]); + } + Object[] passed = new Object[] {new Boolean(ext.isCritical()), + value}; + CertAttrSet crlExt = (CertAttrSet)cons.newInstance(passed); + map.put(crlExt.getName(), crlExt); + addElement(crlExt); + + } catch (InvocationTargetException invk) { + throw new X509ExtensionException( + invk.getTargetException().getMessage()); + + } catch (Exception e) { + throw new X509ExtensionException(e.toString()); + } + } + + /** + * Default constructor. + */ + public CRLExtensions() { + map = new Hashtable(); + } + + /** + * Create the object, decoding the values from the passed DER stream. + * + * @param in the DerInputStream to read the Extension from. + * @exception CRLException on decoding errors. + * @exception X509ExtensionException on extension handling errors. + */ + public CRLExtensions(DerInputStream in) + throws CRLException, X509ExtensionException { + + map = new Hashtable(); + try { + DerValue[] exts = in.getSequence(5); + + for (int i = 0; i < exts.length; i++) { + Extension ext = new Extension(exts[i]); + parseExtension(ext); + } + } catch (IOException e) { + throw new CRLException("Parsing error: " + e.toString()); + } + } + + /** + * Decode the extensions from the InputStream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception CRLException on decoding or validity errors. + * @exception X509ExtensionException on extension handling errors. + */ + public void decode(InputStream in) + throws CRLException, X509ExtensionException { + try { + DerValue val = new DerValue(in); + DerInputStream str = val.toDerInputStream(); + + map = new Hashtable(); + DerValue[] exts = str.getSequence(5); + + for (int i = 0; i < exts.length; i++) { + Extension ext = new Extension(exts[i]); + parseExtension(ext); + } + } catch (IOException e) { + throw new CRLException("Parsing error: " + e.toString()); + } + } + + /** + * Encode the extensions in DER form to the stream. + * + * @param out the DerOutputStream to marshal the contents to. + * @param isExplicit the tag indicating whether this is an entry + * extension or a CRL extension. + * @exception CRLException on encoding errors. + */ + public void encode(OutputStream out, boolean isExplicit) + throws CRLException { + try { + // #381559 + if (size() == 0) + return; + DerOutputStream extOut = new DerOutputStream(); + for (int i = 0; i < size(); i++) { + Object thisOne = elementAt(i); + if (thisOne instanceof CertAttrSet) + ((CertAttrSet)thisOne).encode(extOut); + else if (thisOne instanceof Extension) + ((Extension)thisOne).encode(extOut); + else + throw new CRLException("Illegal extension object"); + } + + DerOutputStream seq = new DerOutputStream(); + seq.write(DerValue.tag_Sequence, extOut); + + DerOutputStream tmp = new DerOutputStream(); + if (isExplicit) + tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte)0), seq); + else + tmp = seq; + + out.write(tmp.toByteArray()); + } catch (IOException e) { + throw new CRLException("Encoding error: " + e.toString()); + } catch (CertificateException e) { + throw new CRLException("Encoding error: " + e.toString()); + } + } + + /** + * Get the extension with this alias. + * + * @param alias the identifier string for the extension to retrieve. + * @exception X509ExtensionException on extension handling errors. + */ + public Extension get(String alias) throws X509ExtensionException { + X509AttributeName attr = new X509AttributeName(alias); + String name; + String id = attr.getPrefix(); + if (id.equalsIgnoreCase(X509CertImpl.NAME)) { // fully qualified + int index = alias.lastIndexOf("."); + name = alias.substring(index + 1); + } else + name = alias; + Extension ext = (Extension)map.get(name); + if (ext == null) + throw new X509ExtensionException("No extension found with name: " + + alias); + return ext; + } + + /** + * Set the extension value with this alias. + * + * @param alias the identifier string for the extension to set. + * @param obj the Object to set the extension identified by the + * alias. + * @exception IOException on errors. + */ + public void set(String alias, Object obj) throws IOException { + map.put(alias, obj); + addElement(obj); + } + + /** + * Return an enumeration of names of the extensions. + * @return an enumeration of the names of the extensions in this CRL. + */ + public Enumeration getElements () { + return (map.elements()); + } +} diff --git a/pki/base/util/src/netscape/security/x509/CRLNumberExtension.java b/pki/base/util/src/netscape/security/x509/CRLNumberExtension.java new file mode 100755 index 00000000..67cd98e5 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CRLNumberExtension.java @@ -0,0 +1,218 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.math.BigInteger; +import java.util.Enumeration; + +import netscape.security.util.*; + +/** + * Represent the CRL Number Extension. + * + * <p>This extension, if present, conveys a monotonically increasing + * sequence number for each CRL issued by a given CA through a specific + * CA X.500 Directory entry or CRL distribution point. This extension + * allows users to easily determine when a particular CRL supersedes + * another CRL. + * + * @author Hemma Prafullchandra + * @version 1.2 + * @see Extension + * @see CertAttrSet + */ +public class CRLNumberExtension extends Extension +implements CertAttrSet { + + /** + * Attribute name. + */ + public static final String NAME = "CRLNumber"; + public static final String NUMBER = "value"; + + private BigInt crlNumber = null; + + // Encode this extension value + private void encodeThis() throws IOException { + if (crlNumber == null) + throw new IOException("Unintialized CRL number extension"); + DerOutputStream os = new DerOutputStream(); + os.putInteger(this.crlNumber); + this.extensionValue = os.toByteArray(); + } + + /** + * Create a CRLNumberExtension with the integer value . + * The criticality is set to false. + * + * @param crlNum the value to be set for the extension. + */ + public CRLNumberExtension(int crlNum) throws IOException { + this.crlNumber = new BigInt(crlNum); + this.extensionId = PKIXExtensions.CRLNumber_Id; + this.critical = false; + encodeThis(); + } + + /** + * Create a CRLNumberExtension with the BigInteger value . + * The criticality is set to false. + * + * @param crlNum the value to be set for the extension. + */ + public CRLNumberExtension(BigInteger crlNum) throws IOException { + this.crlNumber = new BigInt(crlNum); + this.extensionId = PKIXExtensions.CRLNumber_Id; + this.critical = false; + encodeThis(); + } + + /** + * Create a CRLNumberExtension with the BigInteger value . + * + * @param critical true if the extension is to be treated as critical. + * @param crlNum the value to be set for the extension. + */ + public CRLNumberExtension(Boolean critical, BigInteger crlNum) throws IOException { + this.crlNumber = new BigInt(crlNum); + this.extensionId = PKIXExtensions.CRLNumber_Id; + this.critical = critical.booleanValue(); + encodeThis(); + } + + /** + * Create the extension from the passed DER encoded value of the same. + * + * @param critical true if the extension is to be treated as critical. + * @param value Array of DER encoded bytes of the actual value. + * @exception IOException on error. + */ + public CRLNumberExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.CRLNumber_Id; + this.critical = critical.booleanValue(); + + int len = Array.getLength(value); + byte[] extValue = new byte[len]; + for (int i = 0; i < len; i++) { + extValue[i] = Array.getByte(value, i); + } + this.extensionValue = extValue; + DerValue val = new DerValue(extValue); + this.crlNumber = val.getInteger(); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (name.equalsIgnoreCase(NUMBER)) { + if (!(obj instanceof BigInteger)) { + throw new IOException("Attribute must be of type BigInteger."); + } + crlNumber = new BigInt((BigInteger)obj); + } else { + throw new IOException("Attribute name not recognized by" + + " CertAttrSet:CRLNumber."); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(NUMBER)) { + if (crlNumber == null) return null; + else return crlNumber.toBigInteger(); + } else { + throw new IOException("Attribute name not recognized by" + + " CertAttrSet:CRLNumber."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(NUMBER)) { + crlNumber = null; + } else { + throw new IOException("Attribute name not recognized by" + + " CertAttrSet:CRLNumber."); + } + } + + /** + * Returns a printable representation of the CRLNumberExtension. + */ + public String toString() { + String s = super.toString() + "CRL Number: " + + ((crlNumber == null) ? "" : crlNumber.toString()) + + "\n"; + return (s); + } + + /** + * Decode the extension from the InputStream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception IOException on decoding or validity errors. + */ + public void decode(InputStream in) throws IOException { + throw new IOException("Method not to be called directly."); + } + + /** + * Write the extension to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + + if (this.extensionValue == null) { + this.extensionId = PKIXExtensions.CRLNumber_Id; + this.critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements () { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(NUMBER); + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName () { + return (NAME); + } +} diff --git a/pki/base/util/src/netscape/security/x509/CRLReasonExtension.java b/pki/base/util/src/netscape/security/x509/CRLReasonExtension.java new file mode 100644 index 00000000..a3ec235b --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CRLReasonExtension.java @@ -0,0 +1,226 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.util.Enumeration; + +import netscape.security.util.*; + +/** + * Represent the CRLReason Extension of CRL entry. + * + * <p>This extension, if present, defines the identifies + * the reason for the certificate revocation. + * + * @author galperin + * @version $Revision: 14564 $, $Date: 2007-05-01 10:40:13 -0700 (Tue, 01 May 2007) $ + * @see Extension + * @see CertAttrSet + */ + +public final class CRLReasonExtension extends Extension implements CertAttrSet { + + /** + * Canned instances for all revocation reasons + */ + public static final CRLReasonExtension UNSPECIFIED = new CRLReasonExtension(RevocationReason.UNSPECIFIED); + public static final CRLReasonExtension KEY_COMPROMISE = new CRLReasonExtension(RevocationReason.KEY_COMPROMISE); + public static final CRLReasonExtension CA_COMPROMISE = new CRLReasonExtension(RevocationReason.CA_COMPROMISE); + public static final CRLReasonExtension AFFILIATION_CHANGED = new CRLReasonExtension(RevocationReason.AFFILIATION_CHANGED); + public static final CRLReasonExtension SUPERSEDED = new CRLReasonExtension(RevocationReason.SUPERSEDED); + public static final CRLReasonExtension CESSATION_OF_OPERATION = new CRLReasonExtension(RevocationReason.CESSATION_OF_OPERATION); + public static final CRLReasonExtension CERTIFICATE_HOLD = new CRLReasonExtension(RevocationReason.CERTIFICATE_HOLD); + public static final CRLReasonExtension REMOVE_FROM_CRL = new CRLReasonExtension(RevocationReason.REMOVE_FROM_CRL); + + /** + * Attribute names. + */ + public static final String NAME = "CRLReason"; + public static final String REASON = "value"; + + private RevocationReason mReason = null; + + public RevocationReason getReason() { + return mReason; + } + + /** + * Default constructor + * + */ + + public CRLReasonExtension() { + this.extensionId = PKIXExtensions.ReasonCode_Id; + this.critical = false; + mReason = null; + } + + + /** + * Create extension value for specific revocation reason + * + */ + + public CRLReasonExtension(RevocationReason reason) { + this.extensionId = PKIXExtensions.ReasonCode_Id; + this.critical = false; + mReason = reason; + } + + public CRLReasonExtension(Boolean critical, RevocationReason reason) + throws IOException { + this.extensionId = PKIXExtensions.ReasonCode_Id; + this.critical = critical.booleanValue(); + mReason = reason; + } + + /** + * Create the object from the passed DER encoded value. + * + * @param derVal the DerValue decoded from the stream. + * @exception IOException on decoding errors. + */ + public CRLReasonExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.ReasonCode_Id; + this.critical = critical.booleanValue(); + + byte[] extValue = (byte[])((byte[])value).clone(); + this.extensionValue = extValue; + DerValue val = new DerValue(extValue); + int reasonCode = val.getEnumerated(); + mReason = RevocationReason.fromInt(reasonCode); + if (mReason == null) + throw new IOException("Unknown revocation reason value " + reasonCode); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (!(obj instanceof RevocationReason)) { + throw new IOException("Attribute must be of type RevocationReason."); + } + + if (name.equalsIgnoreCase(REASON)) { + mReason = (RevocationReason)obj; + } else { + throw new IOException("Name not recognized by CRLReason"); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(REASON)) { + return mReason; + } else { + throw new IOException("Name not recognized by CRLReason"); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(REASON)) { + mReason = null; + } else { + throw new IOException("Name not recognized by CRLReason"); + } + } + + /** + * Returns a printable representation of the ReasonFlags. + */ + public String toString() { + String s = super.toString() + "CRL Reason [" + mReason + "]\n"; + return (s); + } + + /** + * Decode the extension from the InputStream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception IOException on decoding or validity errors. + */ + public void decode(InputStream in) throws IOException { + throw new IOException("Method not to be called directly."); + } + + // Encode this extension value + private void encodeThis() throws IOException { + if (mReason == null) + throw new IOException("Unintialized CRLReason extension"); + DerOutputStream os = new DerOutputStream(); + os.putEnumerated(mReason.toInt()); + this.extensionValue = os.toByteArray(); + } + + + /** + * Write the extension to the DerOutputStream. + * + * @param out the OutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + + if (this.extensionValue == null) { + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements () { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(REASON); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName () { + return (NAME); + } + + public boolean equals(Object other) { + if (this == other) + return true; + else if (other instanceof CRLReasonExtension) + return ((CRLReasonExtension)other).mReason == mReason && + ((CRLReasonExtension)other).critical == critical; + else + return false; + } + +} diff --git a/pki/base/util/src/netscape/security/x509/CertAndKeyGen.java b/pki/base/util/src/netscape/security/x509/CertAndKeyGen.java new file mode 100644 index 00000000..75d8d4a3 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CertAndKeyGen.java @@ -0,0 +1,299 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.security.cert.X509Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateEncodingException; +import java.security.*; +import java.util.Date; + +import netscape.security.util.BigInt; +import netscape.security.pkcs.PKCS10; + + +/** + * Generate a pair of keys, and provide access to them. This class is + * provided primarily for ease of use. + * + * <P>This provides some simple certificate management functionality. + * Specifically, it allows you to create self-signed X.509 certificates + * as well as PKCS 10 based certificate signing requests. + * + * <P>Keys for some public key signature algorithms have algorithm + * parameters, such as DSS/DSA. Some sites' Certificate Authorities + * adopt fixed algorithm parameters, which speeds up some operations + * including key generation and signing. <em>At this time, this interface + * does not provide a way to provide such algorithm parameters, e.g. + * by providing the CA certificate which includes those parameters.</em> + * + * <P>Also, note that at this time only signature-capable keys may be + * acquired through this interface. Diffie-Hellman keys, used for secure + * key exchange, may be supported later. + * + * @author David Brownell + * @author Hemma Prafullchandra + * @version 1.44 + * @see PKCS10 + * @see X509CertImpl + */ +public final class CertAndKeyGen { + /** + * Creates a CertAndKeyGen object for a particular key type + * and signature algorithm. + * + * @param keyType type of key, e.g. "RSA", "DSA" + * @param sigAlg name of the signature algorithm, e.g. "MD5WithRSA", + * "MD2WithRSA", "SHAwithDSA". + * @exception NoSuchAlgorithmException on unrecognized algorithms. + */ + public CertAndKeyGen (String keyType, String sigAlg) + throws NoSuchAlgorithmException + { + keyGen = KeyPairGenerator.getInstance(keyType); + this.sigAlg = sigAlg; + } + + /** + * Sets the source of random numbers used when generating keys. + * If you do not provide one, a system default facility is used. + * You may wish to provide your own source of random numbers + * to get a reproducible sequence of keys and signatures, or + * because you may be able to take advantage of strong sources + * of randomness/entropy in your environment. + * + * @deprecated All random numbers come from PKCS #11 now. + */ + public void setRandom (SecureRandom generator) + { + } + + // want "public void generate (X509Certificate)" ... inherit DSA/D-H param + + /** + * Generates a random public/private key pair, with a given key + * size. Different algorithms provide different degrees of security + * for the same key size, because of the "work factor" involved in + * brute force attacks. As computers become faster, it becomes + * easier to perform such attacks. Small keys are to be avoided. + * + * <P>Note that not all values of "keyBits" are valid for all + * algorithms, and not all public key algorithms are currently + * supported for use in X.509 certificates. If the algorithm + * you specified does not produce X.509 compatible keys, an + * invalid key exception is thrown. + * + * @param keyBits the number of bits in the keys. + * @exception InvalidKeyException if the environment does not + * provide X.509 public keys for this signature algorithm. + */ + public void generate (int keyBits) + throws InvalidKeyException + { + KeyPair pair; + + try { + keyGen.initialize (keyBits); + pair = keyGen.generateKeyPair (); + + } catch (Exception e) { + throw new IllegalArgumentException (e.getMessage ()); + } + + PublicKey publicKey = pair.getPublic(); + + if (publicKey instanceof X509Key) { + this.publicKey = (X509Key) publicKey; + + } else { + throw new InvalidKeyException ("public key " + publicKey + + " not an X509Key."); + } + privateKey = pair.getPrivate (); + } + + + /** + * Returns the public key of the generated key pair. + */ + public X509Key getPublicKey () + { + return publicKey; + } + + + /** + * Returns the private key of the generated key pair. + * + * <P><STRONG><em>Be extremely careful when handling private keys. + * When private keys are not kept secret, they lose their ability + * to securely authenticate specific entities ... that is a huge + * security risk!</em></STRONG> + */ + public PrivateKey getPrivateKey () + { + return privateKey; + } + + + /** + * Returns a self-signed X.509v1 certificate for the public key. + * The certificate is immediately valid. + * + * <P>Such certificates normally are used to identify a "Certificate + * Authority" (CA). Accordingly, they will not always be accepted by + * other parties. However, such certificates are also useful when + * you are bootstrapping your security infrastructure, or deploying + * system prototypes. + * + * @deprecated Use the new <a href = + * "#getSelfCertificate(netscape.security.x509.X500Name, long)"> + * + * @param myname X.500 name of the subject (who is also the issuer) + * @param validity how long the certificate should be valid, in seconds + */ + public X509Cert getSelfCert (X500Name myname, long validity) + throws InvalidKeyException, SignatureException, NoSuchAlgorithmException + { + X509Certificate cert; + + try { + cert = getSelfCertificate(myname, validity); + return new X509Cert(cert.getEncoded()); + } catch (CertificateException e) { + throw new SignatureException(e.getMessage()); + } catch (NoSuchProviderException e) { + throw new NoSuchAlgorithmException(e.getMessage()); + } catch (IOException e) { + throw new SignatureException(e.getMessage()); + } + } + + + /** + * Returns a self-signed X.509v3 certificate for the public key. + * The certificate is immediately valid. No extensions. + * + * <P>Such certificates normally are used to identify a "Certificate + * Authority" (CA). Accordingly, they will not always be accepted by + * other parties. However, such certificates are also useful when + * you are bootstrapping your security infrastructure, or deploying + * system prototypes. + * + * @param myname X.500 name of the subject (who is also the issuer) + * @param validity how long the certificate should be valid, in seconds + * @exception CertificateException on certificate handling errors. + * @exception InvalidKeyException on key handling errors. + * @exception SignatureException on signature handling errors. + * @exception NoSuchAlgorithmException on unrecognized algorithms. + * @exception NoSuchProviderException on unrecognized providers. + */ + public X509Certificate getSelfCertificate (X500Name myname, long validity) + throws CertificateException, InvalidKeyException, SignatureException, + NoSuchAlgorithmException, NoSuchProviderException + { + X500Signer issuer; + X509CertImpl cert; + Date firstDate, lastDate; + + try { + issuer = getSigner (myname); + + firstDate = new Date (); + lastDate = new Date (); + lastDate.setTime (lastDate.getTime () + validity * 1000); + + CertificateValidity interval = + new CertificateValidity(firstDate,lastDate); + + X509CertInfo info = new X509CertInfo(); + // Add all mandatory attributes + info.set(X509CertInfo.VERSION, + new CertificateVersion(CertificateVersion.V1)); + info.set(X509CertInfo.SERIAL_NUMBER, + new CertificateSerialNumber((int)(firstDate.getTime()/1000))); + AlgorithmId algID = issuer.getAlgorithmId(); + info.set(X509CertInfo.ALGORITHM_ID, + new CertificateAlgorithmId(algID)); + info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(myname)); + info.set(X509CertInfo.KEY, new CertificateX509Key(publicKey)); + info.set(X509CertInfo.VALIDITY, interval); + info.set(X509CertInfo.ISSUER, + new CertificateIssuerName(issuer.getSigner())); + + cert = new X509CertImpl(info); + cert.sign(privateKey, algID.getName()); + + return (X509Certificate)cert; + + } catch (IOException e) { + throw new CertificateEncodingException("getSelfCert: " + + e.getMessage()); + } + } + + /** + * Returns a PKCS #10 certificate request. The caller uses either + * <code>PKCS10.print</code> or <code>PKCS10.toByteArray</code> + * operations on the result, to get the request in an appropriate + * transmission format. + * + * <P>PKCS #10 certificate requests are sent, along with some proof + * of identity, to Certificate Authorities (CAs) which then issue + * X.509 public key certificates. + * + * @param myname X.500 name of the subject + * @exception InvalidKeyException on key handling errors. + * @exception SignatureException on signature handling errors. + */ + public PKCS10 getCertRequest (X500Name myname) + throws InvalidKeyException, SignatureException + { + PKCS10 req = new PKCS10 (publicKey); + + try { + req.encodeAndSign (getSigner (myname)); + + } catch (CertificateException e) { + throw new SignatureException (sigAlg + " CertificateException"); + + } catch (IOException e) { + throw new SignatureException (sigAlg + " IOException"); + + } catch (NoSuchAlgorithmException e) { + // "can't happen" + throw new SignatureException (sigAlg + " unavailable?"); + } + return req; + } + + private X500Signer getSigner (X500Name me) + throws InvalidKeyException, NoSuchAlgorithmException + { + Signature signature = Signature.getInstance(sigAlg); + + signature.initSign (privateKey); + return new X500Signer (signature, me); + } + + private String sigAlg; + private KeyPairGenerator keyGen; + private X509Key publicKey; + private PrivateKey privateKey; +} diff --git a/pki/base/util/src/netscape/security/x509/CertAttrSet.java b/pki/base/util/src/netscape/security/x509/CertAttrSet.java new file mode 100755 index 00000000..53401132 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CertAttrSet.java @@ -0,0 +1,122 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.cert.CertificateException; +import java.util.Enumeration; + +/** + * This interface defines the methods required of a certificate attribute. + * Examples of X.509 certificate attributes are Validity, Issuer_Name, and + * Subject Name. A CertAttrSet may compromise one attribute or many + * attributes. + * <p> + * A CertAttrSet itself can also be comprised of other sub-sets. + * In the case of X.509 V3 certificates, for example, the "extensions" + * attribute has subattributes, such as those for KeyUsage and + * AuthorityKeyIdentifier. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.9 + * @see CertificateException + */ +public interface CertAttrSet { + /** + * Returns a short string describing this certificate attribute. + * + * @return value of this certificate attribute in + * printable form. + */ + String toString(); + + /** + * Encodes the attribute to the output stream in a format + * that can be parsed by the <code>decode</code> method. + * + * @param out the OutputStream to encode the attribute to. + * + * @exception CertificateException on encoding or validity errors. + * @exception IOException on other errors. + */ + void encode(OutputStream out) + throws CertificateException, IOException; + + /** + * Decodes the attribute in the input stream. + * + * @param in the InputStream to read the encoded attribute from. + * + * @exception CertificateException on decoding or validity errors. + * @exception IOException on other errors. + */ + void decode(InputStream in) + throws CertificateException, IOException; + + /** + * Sets an attribute value within this CertAttrSet. + * + * @param name the name of the attribute (e.g. "x509.info.key") + * @param obj the attribute object. + * + * @exception CertificateException on attribute handling errors. + * @exception IOException on other errors. + */ + void set(String name, Object obj) + throws CertificateException, IOException; + + /** + * Gets an attribute value for this CertAttrSet. + * + * @param name the name of the attribute to return. + * + * @exception CertificateException on attribute handling errors. + * @exception IOException on other errors. + */ + Object get(String name) + throws CertificateException, IOException; + + /** + * Deletes an attribute value from this CertAttrSet. + * + * @param name the name of the attribute to delete. + * + * @exception CertificateException on attribute handling errors. + * @exception IOException on other errors. + */ + void delete(String name) + throws CertificateException, IOException; + + /** + * Returns an enumeration of the names of the attributes existing within + * this attribute. + * + * @return an enumeration of the attribute names. + */ + Enumeration getElements(); + + /** + * Returns the name (identifier) of this CertAttrSet. + * + * @return the name of this CertAttrSet. + */ + String getName(); +} diff --git a/pki/base/util/src/netscape/security/x509/CertException.java b/pki/base/util/src/netscape/security/x509/CertException.java new file mode 100644 index 00000000..fc47ed7b --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CertException.java @@ -0,0 +1,160 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +/** + * CertException indicates one of a variety of certificate problems. + * + * @version 1.18 + * + * @author David Brownell + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class CertException extends SecurityException { + + // Zero is reserved. + + /** Indicates that the signature in the certificate is not valid. */ + public static final int verf_INVALID_SIG = 1; + + /** Indicates that the certificate was revoked, and so is invalid. */ + public static final int verf_INVALID_REVOKED = 2; + + /** Indicates that the certificate is not yet valid. */ + public static final int verf_INVALID_NOTBEFORE = 3; + + /** Indicates that the certificate has expired and so is not valid. */ + public static final int verf_INVALID_EXPIRED = 4; + + /** Indicates that a certificate authority in the certification + * chain is not trusted. */ + public static final int verf_CA_UNTRUSTED = 5; + + /** Indicates that the certification chain is too long. */ + public static final int verf_CHAIN_LENGTH = 6; + + /** Indicates an error parsing the ASN.1/DER encoding of the certificate. */ + public static final int verf_PARSE_ERROR = 7; + + /** Indicates an error constructing a certificate or certificate chain. */ + public static final int err_CONSTRUCTION = 8; + + /** Indicates a problem with the public key */ + public static final int err_INVALID_PUBLIC_KEY = 9; + + /** Indicates a problem with the certificate version */ + public static final int err_INVALID_VERSION = 10; + + /** Indicates a problem with the certificate format */ + public static final int err_INVALID_FORMAT = 11; + + /** Indicates a problem with the certificate encoding */ + public static final int err_ENCODING = 12; + + // Private data members + private int verfCode; + private String moreData; + + + /** + * Constructs a certificate exception using an error code + * (<code>verf_*</code>) and a string describing the context + * of the error. + */ + public CertException(int code, String moredata) + { + verfCode = code; + moreData = moredata; + } + + /** + * Constructs a certificate exception using just an error code, + * without a string describing the context. + */ + public CertException(int code) + { + verfCode = code; + } + + /** + * Returns the error code with which the exception was created. + */ + public int getVerfCode() { return verfCode; } + + /** + * Returns a string describing the context in which the exception + * was reported. + */ + public String getMoreData() { return moreData; } + + /** + * Return a string corresponding to the error code used to create + * this exception. + */ + public String getVerfDescription() + { + switch (verfCode) { + case verf_INVALID_SIG: + return "The signature in the certificate is not valid."; + case verf_INVALID_REVOKED: + return "The certificate has been revoked."; + case verf_INVALID_NOTBEFORE: + return "The certificate is not yet valid."; + case verf_INVALID_EXPIRED: + return "The certificate has expired."; + case verf_CA_UNTRUSTED: + return "The Authority which issued the certificate is not trusted."; + case verf_CHAIN_LENGTH: + return "The certificate path to a trusted authority is too long."; + case verf_PARSE_ERROR: + return "The certificate could not be parsed."; + case err_CONSTRUCTION: + return "There was an error when constructing the certificate."; + case err_INVALID_PUBLIC_KEY: + return "The public key was not in the correct format."; + case err_INVALID_VERSION: + return "The certificate has an invalid version number."; + case err_INVALID_FORMAT: + return "The certificate has an invalid format."; + case err_ENCODING: + return "Problem encountered while encoding the data."; + + default: + return "Unknown code: " + verfCode; + } + } + + /** + * Returns a string describing the certificate exception. + */ + public String toString() + { + return "[Certificate Exception: " + getMessage() + "]"; + } + + /** + * Returns a string describing the certificate exception. + */ + public String getMessage() + { + return getVerfDescription() + + ( (moreData != null) + ? ( "\n (" + moreData + ")" ) : "" ); + } +} diff --git a/pki/base/util/src/netscape/security/x509/CertParseError.java b/pki/base/util/src/netscape/security/x509/CertParseError.java new file mode 100644 index 00000000..81a7a377 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CertParseError.java @@ -0,0 +1,38 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +//back out these changes until backwards compatibility with +//CertException is not an issue. +//import java.security.CertificateException; + +/** + * CertException indicates one of a variety of certificate problems. + * + * @version 1.7 + * @author David Brownell + */ + +class CertParseError extends CertException +{ + CertParseError (String where) + { + super (CertException.verf_PARSE_ERROR, where); + } +} + diff --git a/pki/base/util/src/netscape/security/x509/CertificateAlgorithmId.java b/pki/base/util/src/netscape/security/x509/CertificateAlgorithmId.java new file mode 100644 index 00000000..3759a2ea --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CertificateAlgorithmId.java @@ -0,0 +1,178 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Enumeration; + +import netscape.security.util.*; + +/** + * This class defines the AlgorithmId for the Certificate. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.7 + */ +public class CertificateAlgorithmId implements CertAttrSet, Serializable { + private AlgorithmId algId; + + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.algorithmID"; + /** + * Sub attributes name for this CertAttrSet. + */ + public static final String NAME = "algorithmID"; + public static final String ALGORITHM = "algorithm"; + + /** + * Default constructor for the certificate attribute. + * + * @param algId the Algorithm identifier + */ + public CertificateAlgorithmId(AlgorithmId algId) { + this.algId = algId; + } + + /** + * Create the object, decoding the values from the passed DER stream. + * + * @param in the DerInputStream to read the serial number from. + * @exception IOException on decoding errors. + */ + public CertificateAlgorithmId(DerInputStream in) throws IOException { + DerValue val = in.getDerValue(); + algId = AlgorithmId.parse(val); + } + + /** + * Create the object, decoding the values from the passed stream. + * + * @param in the InputStream to read the serial number from. + * @exception IOException on decoding errors. + */ + public CertificateAlgorithmId(InputStream in) throws IOException { + DerValue val = new DerValue(in); + algId = AlgorithmId.parse(val); + } + + /** + * Return the algorithm identifier as user readable string. + */ + public String toString() { + if (algId == null) return ""; + return (algId.toString() + + ", OID = " + (algId.getOID()).toString() + "\n"); + } + + private synchronized void writeObject(ObjectOutputStream stream) + throws IOException { + encode(stream); + } + + private synchronized void readObject(ObjectInputStream stream) + throws IOException { + decode(stream); + } + + /** + * Encode the algorithm identifier in DER form to the stream. + * + * @param out the DerOutputStream to marshal the contents to. + * @exception IOException on errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + algId.encode(tmp); + + out.write(tmp.toByteArray()); + } + + /** + * Decode the algorithm identifier from the passed stream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception IOException on errors. + */ + public void decode(InputStream in) throws IOException { + DerValue derVal = new DerValue(in); + algId = AlgorithmId.parse(derVal); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (!(obj instanceof AlgorithmId)) { + throw new IOException("Attribute must be of type AlgorithmId."); + } + if (name.equalsIgnoreCase(ALGORITHM)) { + algId = (AlgorithmId)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateAlgorithmId."); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(ALGORITHM)) { + return (algId); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateAlgorithmId."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(ALGORITHM)) { + algId = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateAlgorithmId."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(ALGORITHM); + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/pki/base/util/src/netscape/security/x509/CertificateChain.java b/pki/base/util/src/netscape/security/x509/CertificateChain.java new file mode 100644 index 00000000..c86b6dbc --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CertificateChain.java @@ -0,0 +1,135 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; +import java.security.cert.*; +import netscape.security.pkcs.*; +import java.io.*; + +public class CertificateChain implements Serializable +{ + public CertificateChain() { } + + /** + * constructs a certificate chain from a certificate. + * @param cert a certificate + */ + public CertificateChain(X509Certificate cert) + { + mChain = new X509Certificate[1]; + mChain[0] = cert; + } + + /** + * constructs a certificate chain from a X509 certificate array. + * @param chain a certificate array. + */ + public CertificateChain(X509Certificate[] chain) + { + mChain = (X509Certificate[])chain.clone(); + } + + /** + * returns the certificate at specified index in chain. + * @param index the index. + * @return the X509 certificate at the given index. + */ + public X509Certificate getCertificate(int index) + { + return mChain[index]; + } + + /** + * returns the first certificate in chain. + * @return the X509 certificate at the given index. + */ + public X509Certificate getFirstCertificate() + { + return mChain[0]; + } + + /** + * returns the certificate chain as an array of X509 certificates. + * @return an array of X509 Certificates. + */ + public X509Certificate[] getChain() + { + return (X509Certificate[])mChain.clone(); + } + + public void encode(OutputStream out) + throws IOException + { + encode(out, true); + } + + /** + * encode in PKCS7 blob. + */ + public void encode(OutputStream out, boolean sort) + throws IOException + { + PKCS7 p7 = new PKCS7(new AlgorithmId[0], + new ContentInfo(new byte[0]), mChain, + new SignerInfo[0]); + p7.encodeSignedData(out, sort); + } + + /** + * decode from PKCS7 blob. + */ + public void decode(InputStream in) + throws IOException + { + PKCS7 p7 = new PKCS7(in); + mChain = p7.getCertificates(); + } + + /** + * for serialization + */ + private void writeObject(java.io.ObjectOutputStream out) + throws IOException + { + encode(out); + } + + /** + * for serialization + */ + private void readObject(java.io.ObjectInputStream in) + throws IOException + { + decode(in); + } + + /** + * Converts the certificate chain to a readable string. + */ + public String toString() { + String s = "[\n"; + if (mChain == null) + return "[empty]"; + for (int i = 0; i < mChain.length; i++) { + s += mChain[i].toString(); + } + s += "]\n"; + return s; + } + + private X509Certificate[] mChain = null; +} diff --git a/pki/base/util/src/netscape/security/x509/CertificateExtensions.java b/pki/base/util/src/netscape/security/x509/CertificateExtensions.java new file mode 100644 index 00000000..617fc93c --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CertificateExtensions.java @@ -0,0 +1,261 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.security.cert.CertificateException; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import netscape.security.util.*; + +/** + * This class defines the Extensions attribute for the Certificate. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.11 + * @see CertAttrSet + */ +public class CertificateExtensions extends Vector +implements CertAttrSet, Serializable { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.extensions"; + /** + * name + */ + public static final String NAME = "extensions"; + + private Hashtable map; + + // Parse the encoded extension + public void parseExtension(Extension ext) throws IOException { + try { + Class extClass = OIDMap.getClass(ext.getExtensionId()); + if (extClass == null) { // Unsupported extension + if (ext.isCritical()) { + throw new IOException("Unsupported CRITICAL extension: " + + ext.getExtensionId()); + } else { + map.put(ext.getExtensionId().toString(), ext); + addElement(ext); + return; + } + } + Class[] params = {Boolean.class, Object.class}; + Constructor cons = extClass.getConstructor(params); + + byte[] extData = ext.getExtensionValue(); + int extLen = extData.length; + Object value = Array.newInstance(byte.class, extLen); + + for (int i = 0; i < extLen; i++) { + Array.setByte(value, i, extData[i]); + } + Object[] passed = new Object[]{new Boolean(ext.isCritical()), + value}; + CertAttrSet certExt = (CertAttrSet) cons.newInstance(passed); + if (certExt != null && certExt.getName() != null) { + map.put(certExt.getName(), certExt); + addElement(certExt); + } + } catch (NoSuchMethodException nosuch) { + throw new IOException(nosuch.toString()); + } catch (InvocationTargetException invk) { + throw new IOException(invk.getTargetException().toString()); + } catch (Exception e) { + throw new IOException(e.toString()); + } + } + + /** + * Default constructor for the certificate attribute. + */ + public CertificateExtensions() { + map = new Hashtable(); + } + + /** + * Create the object, decoding the values from the passed DER stream. + * + * @param in the DerInputStream to read the Extension from. + * @exception IOException on decoding errors. + */ + public CertificateExtensions(DerInputStream in) + throws IOException { + + map = new Hashtable(); + DerValue[] exts = in.getSequence(5); + + for (int i = 0; i < exts.length; i++) { + Extension ext = new Extension(exts[i]); + parseExtension(ext); + } + } + + /** + * Decode the extensions from the InputStream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception IOException on decoding or validity errors. + */ + public void decode(InputStream in) throws IOException { + DerValue val = new DerValue(in); + DerInputStream str = val.toDerInputStream(); + + map = new Hashtable(); + DerValue[] exts = str.getSequence(5); + + for (int i = 0; i < exts.length; i++) { + Extension ext = new Extension(exts[i]); + parseExtension(ext); + } + } + + /** + * Decode the extensions from the InputStream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception IOException on decoding or validity errors. + */ + public void decodeEx(InputStream in) throws IOException { + DerValue val = new DerValue(in); + DerInputStream str = null; + if (val.isConstructed() && val.isContextSpecific((byte)3)) { + str = val.data; + } else { + str = val.toDerInputStream(); + } + + map = new Hashtable(); + DerValue[] exts = str.getSequence(5); + + for (int i = 0; i < exts.length; i++) { + Extension ext = new Extension(exts[i]); + parseExtension(ext); + } + } + + private synchronized void writeObject(ObjectOutputStream stream) + throws CertificateException, IOException { + encode(stream); + } + + private synchronized void readObject(ObjectInputStream stream) + throws CertificateException, IOException { + decodeEx(stream); + } + + /** + * Encode the extensions in DER form to the stream. + * + * @param out the DerOutputStream to marshal the contents to. + * @exception CertificateException on encoding errors. + * @exception IOException on errors. + */ + public void encode(OutputStream out) + throws CertificateException, IOException { + DerOutputStream extOut = new DerOutputStream(); + for (int i = 0; i < size(); i++) { + Object thisOne = elementAt(i); + if (thisOne instanceof CertAttrSet) + ((CertAttrSet)thisOne).encode(extOut); + else if (thisOne instanceof Extension) + ((Extension)thisOne).encode(extOut); + else + throw new CertificateException("Invalid extension object"); + } + + DerOutputStream seq = new DerOutputStream(); + seq.write(DerValue.tag_Sequence,extOut); + + DerOutputStream tmp = new DerOutputStream(); + tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)3), + seq); + + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + * @param name the extension name used in the cache. + * @param obj the object to set. + * @exception IOException if the object could not be cached. + */ + public void set(String name, Object obj) throws IOException { + map.put(name,obj); + addElement(obj); + } + + /** + * Get the attribute value. + * @param name the extension name used in the lookup. + * @exception IOException if named extension is not found. + */ + public Object get(String name) throws IOException { + Object obj = map.get(name); + if (obj == null) { + throw new IOException("No extension found with name " + name); + } + return (obj); + } + + /** + * Delete the attribute value. + * @param name the extension name used in the lookup. + * @exception IOException if named extension is not found. + */ + public void delete(String name) throws IOException { + Object obj = map.get(name); + if (obj == null) { + throw new IOException("No extension found with name " + name); + } + map.remove(name); + removeElement(obj); + } + + public Enumeration getNames() + { + return map.keys(); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements () { + return (map.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName () { + return (NAME); + } +} diff --git a/pki/base/util/src/netscape/security/x509/CertificateIssuerExtension.java b/pki/base/util/src/netscape/security/x509/CertificateIssuerExtension.java new file mode 100644 index 00000000..b8b8a3ec --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CertificateIssuerExtension.java @@ -0,0 +1,242 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.util.Enumeration; +import java.security.cert.CertificateException; + +import netscape.security.util.*; + + +/** + * Represent the CRL Certificate Issuer Extension. + * + * <p>This CRL entry extension identifies the certificate + * issuer associated with an entry in an indirect CRL, + * i.e. a CRL that has the indirectCRL indicator set + * in its issuing distribution point extension. + * + * @see Extension + * @see CertAttrSet + */ + +public class CertificateIssuerExtension extends Extension + implements CertAttrSet +{ + /** + * Attribute name. + */ + public static final String NAME = "CertificateIssuer"; + public static final String CERTIFICATE_ISSUER = "value"; + + /** + * The Object Identifier for this extension. + */ + public static final String OID = "2.5.29.29"; + + // private data members + GeneralNames names = null; + + static { + try { + OIDMap.addAttribute(CertificateIssuerExtension.class.getName(), + OID, NAME); + } catch (CertificateException e) {} + } + + + // Encode this extension + private void encodeThis() throws IOException { + DerOutputStream os = new DerOutputStream(); + try { + names.encode(os); + } catch (GeneralNamesException e) { + throw new IOException(e.toString()); + } + this.extensionValue = os.toByteArray(); + } + + /** + * Create a CertificateIssuerExtension with the passed GeneralNames + * and criticality. + * + * @param critical true if the extension is to be treated as critical. + * @param names the GeneralNames for the issuer. + * @exception IOException on error. + */ + public CertificateIssuerExtension(Boolean critical, GeneralNames names) + throws IOException + { + this.names = names; + this.extensionId = PKIXExtensions.CertificateIssuer_Id; + this.critical = critical.booleanValue(); + encodeThis(); + } + + /** + * Create a CertificateIssuerExtension with the passed GeneralNames. + * + * @param names the GeneralNames for the issuer. + * @exception IOException on error. + */ + public CertificateIssuerExtension(GeneralNames names) + throws IOException + { + this.names = names; + this.extensionId = PKIXExtensions.CertificateIssuer_Id; + this.critical = true; + encodeThis(); + } + + /** + * Create a default CertificateIssuerExtension. + */ + public CertificateIssuerExtension() { + extensionId = PKIXExtensions.CertificateIssuer_Id; + critical = false; + names = new GeneralNames(); + } + + /** + * Create the extension from the passed DER encoded value. + * + * @param critical true if the extension is to be treated as critical. + * @param value Array of DER encoded bytes of the actual value. + * @exception IOException on error. + */ + public CertificateIssuerExtension(Boolean critical, Object value) + throws IOException + { + this.extensionId = PKIXExtensions.CertificateIssuer_Id; + this.critical = critical.booleanValue(); + + int len = Array.getLength(value); + byte[] extValue = new byte[len]; + for (int i = 0; i < len; i++) { + extValue[i] = Array.getByte(value, i); + } + this.extensionValue = extValue; + DerValue val = new DerValue(extValue); + try { + names = new GeneralNames(val); + } catch (GeneralNamesException e) { + throw new IOException("CertificateIssuerExtension: " + + e.toString()); + } + } + + /** + * Returns a printable representation of the CertificateIssuerName. + */ + public String toString() { + if (names == null) return ""; + String s = super.toString() + "CertificateIssuerName [\n" + + names.toString() + "]\n"; + return (s); + } + + /** + * Decode the extension from the InputStream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception IOException on decoding or validity errors. + */ + public void decode(InputStream in) throws IOException { + throw new IOException("Method not to be called directly."); + } + + /** + * Write the extension to the OutputStream. + * + * @param out the OutputStream to write the extension to. + * @exception IOException on encoding error. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (extensionValue == null) { + extensionId = PKIXExtensions.CertificateIssuer_Id; + critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (name.equalsIgnoreCase(CERTIFICATE_ISSUER)) { + if (!(obj instanceof GeneralNames)) { + throw new IOException("Attribute value should be of" + + " type GeneralNames."); + } + names = (GeneralNames)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateIssuerName."); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(CERTIFICATE_ISSUER)) { + return (names); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateIssuerName."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(CERTIFICATE_ISSUER)) { + names = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateIssuerName."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(CERTIFICATE_ISSUER); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} + diff --git a/pki/base/util/src/netscape/security/x509/CertificateIssuerName.java b/pki/base/util/src/netscape/security/x509/CertificateIssuerName.java new file mode 100644 index 00000000..3c3afe3e --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CertificateIssuerName.java @@ -0,0 +1,168 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Enumeration; + +import netscape.security.util.*; + +/** + * This class defines the X500Name attribute for the Certificate. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.6 + * @see CertAttrSet + */ +public class CertificateIssuerName implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.issuer"; + /** + * Sub attributes name for this CertAttrSet. + */ + public static final String NAME = "issuer"; + public static final String DN_NAME = "dname"; + + // Private data member + private X500Name dnName; + + /** + * Default constructor for the certificate attribute. + * + * @param name the X500Name + */ + public CertificateIssuerName(X500Name name) { + this.dnName = name; + } + + /** + * Create the object, decoding the values from the passed DER stream. + * + * @param in the DerInputStream to read the X500Name from. + * @exception IOException on decoding errors. + */ + public CertificateIssuerName(DerInputStream in) throws IOException { + dnName = new X500Name(in); + } + + /** + * Create the object, decoding the values from the passed stream. + * + * @param in the InputStream to read the X500Name from. + * @exception IOException on decoding errors. + */ + public CertificateIssuerName(InputStream in) throws IOException { + DerValue derVal = new DerValue(in); + dnName = new X500Name(derVal); + } + + /** + * Return the name as user readable string. + */ + public String toString() { + if (dnName == null) return ""; + return(dnName.toString()); + } + + /** + * Encode the name in DER form to the stream. + * + * @param out the DerOutputStream to marshal the contents to. + * @exception IOException on errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + dnName.encode(tmp); + + out.write(tmp.toByteArray()); + } + + /** + * Decode the name in DER form from the stream. + * + * @param in the InputStream to marshal the contents from. + * @exception IOException on errors. + */ + public void decode(InputStream in) throws IOException { + DerValue derVal = new DerValue(in); + dnName = new X500Name(derVal); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (!(obj instanceof X500Name)) { + throw new IOException("Attribute must be of type X500Name."); + } + if (name.equalsIgnoreCase(DN_NAME)) { + this.dnName = (X500Name)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateIssuerName."); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(DN_NAME)) { + return(dnName); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateIssuerName."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(DN_NAME)) { + dnName = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateIssuerName."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(DN_NAME); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return(NAME); + } +} diff --git a/pki/base/util/src/netscape/security/x509/CertificateIssuerUniqueIdentity.java b/pki/base/util/src/netscape/security/x509/CertificateIssuerUniqueIdentity.java new file mode 100644 index 00000000..59103227 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CertificateIssuerUniqueIdentity.java @@ -0,0 +1,181 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Enumeration; + +import netscape.security.util.*; + +/** + * This class defines the subject/issuer unique identity attribute + * for the Certificate. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.6 + * @see CertAttrSet + */ +public class CertificateIssuerUniqueIdentity implements CertAttrSet { + private UniqueIdentity id; + + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.issuerID"; + /** + * Sub attributes name for this CertAttrSet. + */ + public static final String NAME = "issuerID"; + public static final String ID = "id"; + + /** + * Default constructor for the certificate attribute. + * + * @param key the UniqueIdentity + */ + public CertificateIssuerUniqueIdentity(UniqueIdentity id) { + this.id = id; + } + + /** + * Create the object, decoding the values from the passed DER stream. + * + * @param in the DerInputStream to read the UniqueIdentity from. + * @exception IOException on decoding errors. + */ + public CertificateIssuerUniqueIdentity(DerInputStream in) + throws IOException { + id = new UniqueIdentity(in); + } + + /** + * Create the object, decoding the values from the passed stream. + * + * @param in the InputStream to read the UniqueIdentity from. + * @exception IOException on decoding errors. + */ + public CertificateIssuerUniqueIdentity(InputStream in) + throws IOException { + DerValue val = new DerValue(in); + id = new UniqueIdentity(val); + } + + /** + * Create the object, decoding the values from the passed DER value. + * + * @param in the DerValue to read the UniqueIdentity from. + * @exception IOException on decoding errors. + */ + public CertificateIssuerUniqueIdentity(DerValue val) + throws IOException { + id = new UniqueIdentity(val); + } + + /** + * Return the identity as user readable string. + */ + public String toString() { + if (id == null) return ""; + return (id.toString()); + } + + /** + * Decode the identity in DER form from the stream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception IOException on errors. + */ + public void decode(InputStream in) throws IOException { + DerValue val = new DerValue(in); + id = new UniqueIdentity(val); + } + + /** + * Encode the identity in DER form to the stream. + * + * @param out the DerOutputStream to marshal the contents to. + * @exception IOException on errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + id.encode(tmp,DerValue.createTag(DerValue.TAG_CONTEXT,false,(byte)1)); + + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (!(obj instanceof UniqueIdentity)) { + throw new IOException("Attribute must be of type UniqueIdentity."); + } + if (name.equalsIgnoreCase(ID)) { + id = (UniqueIdentity)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet: CertificateIssuerUniqueIdentity."); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(ID)) { + return (id); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet: CertificateIssuerUniqueIdentity."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(ID)) { + id = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet: CertificateIssuerUniqueIdentity."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(ID); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/pki/base/util/src/netscape/security/x509/CertificatePoliciesExtension.java b/pki/base/util/src/netscape/security/x509/CertificatePoliciesExtension.java new file mode 100644 index 00000000..8ed8cdc4 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CertificatePoliciesExtension.java @@ -0,0 +1,324 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.security.cert.CertificateException; +import java.security.cert.CertificateParsingException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.util.*; +import netscape.security.util.*; + +/** + * This class defines the Certificate Policies Extension. + * + * <p>The certificate policies extension conatins a sequence of policy + * information terms, each of which consists of an object identifier + * (OID) and optional qualifiers. These policy information terms + * indicate the policy under which the certificate has been issued and + * the purposes for which the certificate may be used. Aplications with + * specific policy requirements are expected to have a list of those + * policies which they will accept and to compare the policy OIDs in the + * certificate to that list. If this extension is critical, the path + * validation software must be able to interpret this extension, or must + * reject the certificate. + * + * <pre> + * CertificatePolicies ::= SEQUENECE OF PolicyInformation + * </pre> + * + *@author Christine Ho + *@see Extension + *@see CertAttrSet + */ +public class CertificatePoliciesExtension extends Extension +implements CertAttrSet { + + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.extensions.CertificatePolicies"; + /** + * Attribute names. + */ + public static final String NAME = "CertificatePolicies"; + public static final String INFOS = "infos"; + + + // Private data members + private Vector mInfos; + + // Encode this extension value + private void encodeThis() throws IOException { + DerOutputStream os = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + + for (int i = 0; i < mInfos.size(); i++) { + ((CertificatePolicyInfo)mInfos.elementAt(i)).encode(tmp); + } + os.write(DerValue.tag_Sequence,tmp); + extensionValue = os.toByteArray(); + } + + public CertificatePoliciesExtension(boolean critical, Vector infos) throws IOException { + this.mInfos = infos; + this.extensionId = PKIXExtensions.CertificatePolicies_Id; + this.critical = critical; + encodeThis(); + } + + /** + * Create a CertificatePolicies with the Vector of CertificatePolicyInfo. + * + * @param infos the Vector of CertificatePolicyInfo. + */ + public CertificatePoliciesExtension(Vector infos) throws IOException { + this.mInfos = infos; + this.extensionId = PKIXExtensions.CertificatePolicies_Id; + this.critical = false; + encodeThis(); + } + + /** + * Create a default CertificatePoliciesExtension. + */ + public CertificatePoliciesExtension() { + this.extensionId = PKIXExtensions.CertificatePolicies_Id; + critical = false; + mInfos = new Vector(1,1); + } + + /** + * Create the extension from the passed DER encoded value. + * + * @param critical true if the extension is to be treated as critical. + * @param value Array of DER encoded bytes of the actual value. + * @exception IOException on error. + */ + public CertificatePoliciesExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.CertificatePolicies_Id; + this.critical = critical.booleanValue(); + + int len = Array.getLength(value); + byte [] extValue = new byte[len]; + for (int i = 0; i < len; i++) { + extValue[i] = Array.getByte(value, i); + } + this.extensionValue = extValue; + DerValue val = new DerValue(extValue); + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for " + + "CertificatePoliciesExtension."); + } + mInfos = new Vector(1, 1); + while (val.data.available() != 0) { + DerValue seq = val.data.getDerValue(); + CertificatePolicyInfo info = new CertificatePolicyInfo(seq); + mInfos.addElement(info); + } + } + + /** + * Returns a printable representation of the policy extension. + */ + public String toString() { + if (mInfos == null) return ""; + String s = super.toString() + "Certificate Policies [\n" + + mInfos.toString() + "]\n"; + + return (s); + } + + /** + * Write the extension to the OutputStream. + * + * @param out the OutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (extensionValue == null) { + extensionId = PKIXExtensions.CertificatePolicies_Id; + critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Decode the extension from the InputStream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception IOException on decoding or validity errors. + */ + public void decode(InputStream in) throws IOException { + throw new IOException("Method not to be called directly."); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + clearValue(); + if (name.equalsIgnoreCase(INFOS)) { + if (!(obj instanceof Vector)) { + throw new IOException("Attribute value should be of" + + " type Vector."); + } + mInfos = (Vector)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificatePoliciesExtension."); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(INFOS)) { + return (mInfos); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificatePoliciesExtension."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(INFOS)) { + mInfos = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificatePoliciesExtension."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements () { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(mInfos); + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName () { + return (NAME); + } + + public static void main(String args[]) + { + +/** From ASN.1 dump + + 0 30 133: SEQUENCE { + 3 30 45: . SEQUENCE { + 5 06 3: . . OBJECT IDENTIFIER '1 2 3 5' + 10 30 38: . . SEQUENCE { + 12 30 36: . . . SEQUENCE { + 14 06 8: . . . . OBJECT IDENTIFIER cps (1 3 6 1 5 5 7 2 1) + : . . . . . (PKIX policy qualifier) + 24 16 24: . . . . IA5String 'http://home.netscape.com' + : . . . . } + : . . . } + : . . } + 50 30 84: . SEQUENCE { + 52 06 2: . . OBJECT IDENTIFIER '2 3 5' + 56 30 78: . . SEQUENCE { + 58 30 36: . . . SEQUENCE { + 60 06 8: . . . . OBJECT IDENTIFIER cps (1 3 6 1 5 5 7 2 1) + : . . . . . (PKIX policy qualifier) + 70 16 24: . . . . IA5String 'http://home.netscape.com' + : . . . . } + 96 30 38: . . . SEQUENCE { + 98 06 8: . . . . OBJECT IDENTIFIER unotice (1 3 6 1 5 5 7 2 2) + : . . . . . (PKIX policy qualifier) + 108 30 26: . . . . SEQUENCE { + 110 30 16: . . . . . SEQUENCE { + 112 1E 8: . . . . . . BMPString (1993) '_..o.r.g' + 122 02 1: . . . . . . INTEGER 1 + 125 02 1: . . . . . . INTEGER 2 + : . . . . . . } + 128 1E 6: . . . . . BMPString (1993) '_..d.t' + : . . . . . } + : . . . . } + : . . . } + : . . } + : . } + + **/ + + CertificatePolicyId plcyId0 = new CertificatePolicyId( + new ObjectIdentifier("1.2.3.5") + ); + PolicyQualifiers qualifiers0 = new PolicyQualifiers(); + CPSuri cpsQualifier0 = new CPSuri("http://home.netscape.com"); + PolicyQualifierInfo qualifierInfo0 = new PolicyQualifierInfo( + PolicyQualifierInfo.QT_CPS, + cpsQualifier0 + ); + qualifiers0.add(qualifierInfo0); + CertificatePolicyInfo info0 = new CertificatePolicyInfo( + plcyId0, qualifiers0); + CertificatePolicyId plcyId1 = new CertificatePolicyId( + new ObjectIdentifier("2.3.5") + ); + PolicyQualifiers qualifiers1 = new PolicyQualifiers(); + DisplayText org1 = new DisplayText(DisplayText.tag_BMPString, + "org"); + int nums[] = {1, 2}; + NoticeReference nr1 = new NoticeReference(org1, nums); + DisplayText dt1 = new DisplayText(DisplayText.tag_BMPString, + "dt"); + UserNotice userNotice1 = new UserNotice(nr1, dt1); + PolicyQualifierInfo qualifierInfo1 = new PolicyQualifierInfo( + PolicyQualifierInfo.QT_UNOTICE, + userNotice1 + ); + qualifiers1.add(qualifierInfo0); + qualifiers1.add(qualifierInfo1); + CertificatePolicyInfo info1 = new CertificatePolicyInfo( + plcyId1, qualifiers1); + Vector infos = new Vector(); + infos.addElement(info0); + infos.addElement(info1); + try { + CertificatePoliciesExtension ext = + new CertificatePoliciesExtension(infos); + + // BASE64 encode the whole thing and write it to stdout + System.out.println( com.netscape.osutil.OSUtil.BtoA(ext.getExtensionValue()) ); + } catch (IOException e) { + System.out.println(e.toString()); + } + } +} diff --git a/pki/base/util/src/netscape/security/x509/CertificatePolicyId.java b/pki/base/util/src/netscape/security/x509/CertificatePolicyId.java new file mode 100644 index 00000000..0eb48dc4 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CertificatePolicyId.java @@ -0,0 +1,79 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import netscape.security.util.*; + + +/** + * Represent the CertificatePolicyId ASN.1 object. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.5 + */ +public class CertificatePolicyId implements java.io.Serializable { + private ObjectIdentifier id; + + /** + * Create a CertificatePolicyId with the ObjectIdentifier. + * + * @param id the ObjectIdentifier for the policy id. + */ + public CertificatePolicyId(ObjectIdentifier id) { + this.id = id; + } + + /** + * Create the object from its Der encoded value. + * + * @param val the DER encoded value for the same. + */ + public CertificatePolicyId(DerValue val) throws IOException { + this.id = val.getOID(); + } + + /** + * Return the value of the CertificatePolicyId as an ObjectIdentifier. + */ + public ObjectIdentifier getIdentifier() { + return (id); + } + + /** + * Returns a printable representation of the CertificatePolicyId. + */ + public String toString() { + String s = "CertificatePolicyId: [" + + id.toString() + + "]\n"; + + return (s); + } + + /** + * Write the CertificatePolicyId to the DerOutputStream. + * + * @param out the DerOutputStream to write the object to. + * @exception IOException on errors. + */ + public void encode(DerOutputStream out) throws IOException { + out.putOID(id); + } +} diff --git a/pki/base/util/src/netscape/security/x509/CertificatePolicyInfo.java b/pki/base/util/src/netscape/security/x509/CertificatePolicyInfo.java new file mode 100644 index 00000000..f657d2f8 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CertificatePolicyInfo.java @@ -0,0 +1,104 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import netscape.security.util.*; + +/** + * Represent the CertificatePolicyInformation ASN.1 object. + * + * @author Christine Ho + */ +public class CertificatePolicyInfo implements java.io.Serializable { + private CertificatePolicyId mPolicyIdentifier; + private PolicyQualifiers mPolicyQualifiers; + + /** + * Create a CertificatePolicyInfo with the passed CertificatePolicyId's. + * + * @param id the CertificatePolicyId. + */ + public CertificatePolicyInfo(CertificatePolicyId id) { + this.mPolicyIdentifier = id; + this.mPolicyQualifiers= null; + } + + public CertificatePolicyInfo(CertificatePolicyId id, PolicyQualifiers qualifiers) { + this.mPolicyIdentifier = id; + this.mPolicyQualifiers= qualifiers; + } + + /** + * Create the CertificatePolicyInfo from the DER encoded value. + * + * @param val the DER encoded value of the same. + */ + public CertificatePolicyInfo(DerValue val) throws IOException { + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for CertificatePolicyInfo"); + } + mPolicyIdentifier = new CertificatePolicyId(val.data.getDerValue()); + // The specification is not clear on whether qualifier is + // optional or not. GTE CyberTrust Root certificate has + // no qualifier. + if (val.data.available() == 0) { + mPolicyQualifiers = null; + } else { + mPolicyQualifiers = new PolicyQualifiers(val.data.getDerValue()); + } + } + + /** + * return the policy identifier of the policy info + */ + public CertificatePolicyId getPolicyIdentifier() { + return (mPolicyIdentifier); + } + + public PolicyQualifiers getPolicyQualifiers() { + return mPolicyQualifiers; + } + + /** + * Returns a printable representation of the CertificatePolicyId. + */ + public String toString() { + String s = "CertificatePolicyInfo: [\n" + + "PolicyIdentifier:" + mPolicyIdentifier.toString() + + + "]\n"; + return (s); + } + + /** + * Write the CertificatePolicyInfo to the DerOutputStream. + * + * @param out the DerOutputStream to write the object to. + * @exception IOException on errors. + */ + public void encode(DerOutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + + mPolicyIdentifier.encode(tmp); + if (mPolicyQualifiers != null) { + mPolicyQualifiers.encode(tmp); + } + out.write(DerValue.tag_Sequence,tmp); + } +} diff --git a/pki/base/util/src/netscape/security/x509/CertificatePolicyMap.java b/pki/base/util/src/netscape/security/x509/CertificatePolicyMap.java new file mode 100644 index 00000000..7ed76931 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CertificatePolicyMap.java @@ -0,0 +1,99 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; + +import netscape.security.util.*; + +/** + * Represent the CertificatePolicyMap ASN.1 object. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.5 + */ +public class CertificatePolicyMap { + private CertificatePolicyId issuerDomain; + private CertificatePolicyId subjectDomain; + + /** + * Create a CertificatePolicyMap with the passed CertificatePolicyId's. + * + * @param issuer the CertificatePolicyId for the issuer CA. + * @param subject the CertificatePolicyId for the subject CA. + */ + public CertificatePolicyMap(CertificatePolicyId issuer, + CertificatePolicyId subject) { + this.issuerDomain = issuer; + this.subjectDomain = subject; + } + + /** + * Create the CertificatePolicyMap from the DER encoded value. + * + * @param val the DER encoded value of the same. + */ + public CertificatePolicyMap(DerValue val) throws IOException { + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for CertificatePolicyMap"); + } + issuerDomain = new CertificatePolicyId(val.data.getDerValue()); + subjectDomain = new CertificatePolicyId(val.data.getDerValue()); + } + + /** + * Return the issuer CA part of the policy map. + */ + public CertificatePolicyId getIssuerIdentifier() { + return (issuerDomain); + } + + /** + * Return the subject CA part of the policy map. + */ + public CertificatePolicyId getSubjectIdentifier() { + return (subjectDomain); + } + + /** + * Returns a printable representation of the CertificatePolicyId. + */ + public String toString() { + String s = "CertificatePolicyMap: [\n" + + "IssuerDomain:" + issuerDomain.toString() + + "SubjectDomain:" + subjectDomain.toString() + + "]\n"; + + return (s); + } + + /** + * Write the CertificatePolicyMap to the DerOutputStream. + * + * @param out the DerOutputStream to write the object to. + * @exception IOException on errors. + */ + public void encode(DerOutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + + issuerDomain.encode(tmp); + subjectDomain.encode(tmp); + out.write(DerValue.tag_Sequence,tmp); + } +} diff --git a/pki/base/util/src/netscape/security/x509/CertificatePolicySet.java b/pki/base/util/src/netscape/security/x509/CertificatePolicySet.java new file mode 100644 index 00000000..9c1994e0 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CertificatePolicySet.java @@ -0,0 +1,84 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.util.Vector; + +import netscape.security.util.*; + +/** + * This class defines the certificate policy set ASN.1 object. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.4 + */ +public class CertificatePolicySet { + private Vector ids; + + /** + * The default constructor for this class. + * + * @param ids the sequence of CertificatePolicyId's. + */ + public CertificatePolicySet(Vector ids) { + this.ids = ids; + } + + /** + * Create the object from the DerValue. + * + * @param in the passed DerInputStream. + * @exception IOException on decoding errors. + */ + public CertificatePolicySet(DerInputStream in) throws IOException { + ids = new Vector(1, 1); + DerValue[] seq = in.getSequence(5); + + for (int i = 0; i < seq.length; i++) { + CertificatePolicyId id = new CertificatePolicyId(seq[i]); + ids.addElement(id); + } + } + + /** + * Return printable form of the object. + */ + public String toString() { + String s = "CertificatePolicySet:[\n" + + ids.toString() + + "]\n"; + + return (s); + } + + /** + * Encode the policy set to the output stream. + * + * @param out the DerOutputStream to encode the data to. + */ + public void encode(DerOutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + + for (int i = 0; i < ids.size(); i++) { + ((CertificatePolicyId)ids.elementAt(i)).encode(tmp); + } + out.write(DerValue.tag_Sequence,tmp); + } +} diff --git a/pki/base/util/src/netscape/security/x509/CertificateSerialNumber.java b/pki/base/util/src/netscape/security/x509/CertificateSerialNumber.java new file mode 100644 index 00000000..f14dd49b --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CertificateSerialNumber.java @@ -0,0 +1,187 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.util.Enumeration; + +import netscape.security.util.*; + +/** + * This class defines the SerialNumber attribute for the Certificate. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.6 + * @see CertAttrSet + */ +public class CertificateSerialNumber implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.serialNumber"; + + /** + * Sub attributes name for this CertAttrSet. + */ + public static final String NAME = "serialNumber"; + public static final String NUMBER = "number"; + + private SerialNumber serial; + + /** + * Default constructor for the certificate attribute. + * + * @param serial the serial number for the certificate. + */ + public CertificateSerialNumber(BigInteger num) { + this.serial = new SerialNumber(num); + } + + /** + * Default constructor for the certificate attribute. + * + * @param serial the serial number for the certificate. + */ + public CertificateSerialNumber(int num) { + this.serial = new SerialNumber(num); + } + + /** + * Create the object, decoding the values from the passed DER stream. + * + * @param in the DerInputStream to read the serial number from. + * @exception IOException on decoding errors. + */ + public CertificateSerialNumber(DerInputStream in) throws IOException { + serial = new SerialNumber(in); + } + + /** + * Create the object, decoding the values from the passed stream. + * + * @param in the InputStream to read the serial number from. + * @exception IOException on decoding errors. + */ + public CertificateSerialNumber(InputStream in) throws IOException { + serial = new SerialNumber(in); + } + + /** + * Create the object, decoding the values from the passed DerValue. + * + * @param val the DER encoded value. + * @exception IOException on decoding errors. + */ + public CertificateSerialNumber(DerValue val) throws IOException { + serial = new SerialNumber(val); + } + + /** + * Return the serial number as user readable string. + */ + public String toString() { + if (serial == null) return ""; + return (serial.toString()); + } + + /** + * Encode the serial number in DER form to the stream. + * + * @param out the DerOutputStream to marshal the contents to. + * @exception IOException on errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + serial.encode(tmp); + + out.write(tmp.toByteArray()); + } + + /** + * Decode the serial number in DER form from the stream. + * + * @param in the InputStream to marshal the contents from. + * @exception IOException on errors. + */ + public void decode(InputStream in) throws IOException { + DerValue derVal = new DerValue(in); + serial = new SerialNumber(derVal); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (!(obj instanceof SerialNumber)) { + throw new IOException("Attribute must be of type SerialNumber."); + } + if (name.equalsIgnoreCase(NUMBER)) { + serial = (SerialNumber)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateSerialNumber."); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(NUMBER)) { + return (serial); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateSerialNumber."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(NUMBER)) { + serial = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateSerialNumber."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(NUMBER); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/pki/base/util/src/netscape/security/x509/CertificateSubjectName.java b/pki/base/util/src/netscape/security/x509/CertificateSubjectName.java new file mode 100644 index 00000000..a45d3560 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CertificateSubjectName.java @@ -0,0 +1,193 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Enumeration; + +import netscape.security.util.*; + +/** + * This class defines the X500Name attribute for the Certificate. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.6 + * @see CertAttrSet + */ +public class CertificateSubjectName implements CertAttrSet, Serializable { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.subject"; + /** + * Sub attributes name for this CertAttrSet. + */ + public static final String NAME = "subject"; + public static final String DN_NAME = "dname"; + + // Private data member + private X500Name dnName; + + /** + * Default constructor for the certificate attribute. + * + * @param name the X500Name + */ + public CertificateSubjectName(X500Name name) { + this.dnName = name; + } + + /** + * Create the object, decoding the values from the passed DER stream. + * + * @param in the DerInputStream to read the X500Name from. + * @exception IOException on decoding errors. + */ + public CertificateSubjectName(DerInputStream in) throws IOException { + dnName = new X500Name(in); + } + + /** + * Create the object, decoding the values from the passed stream. + * + * @param in the InputStream to read the X500Name from. + * @exception IOException on decoding errors. + */ + public CertificateSubjectName(InputStream in) throws IOException { + DerValue derVal = new DerValue(in); + dnName = new X500Name(derVal); + } + + /** + * Return the name as user readable string. + */ + public String toString() { + if (dnName == null) return ""; + return(dnName.toString()); + } + + private synchronized void writeObject(ObjectOutputStream stream) + throws IOException { + encode(stream); + } + + private synchronized void readObject(ObjectInputStream stream) + throws IOException { + decodeEx(stream); + } + + /** + * Encode the name in DER form to the stream. + * + * @param out the DerOutputStream to marshal the contents to. + * @exception IOException on errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + dnName.encode(tmp); + + out.write(tmp.toByteArray()); + } + + /** + * Decode the name in DER form from the stream. + * + * @param in the InputStream to marshal the contents from. + * @exception IOException on errors. + */ + public void decodeEx(InputStream in) throws IOException { + DerValue derVal = new DerValue(in); + + // dnName = new X500Name(derVal); + dnName = new X500Name(derVal.toByteArray()); + } + + /** + * Decode the name in DER form from the stream. + * + * @param in the InputStream to marshal the contents from. + * @exception IOException on errors. + */ + public void decode(InputStream in) throws IOException { + DerValue derVal = new DerValue(in); + + dnName = new X500Name(derVal); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (!(obj instanceof X500Name)) { + throw new IOException("Attribute must be of type X500Name."); + } + if (name.equalsIgnoreCase(DN_NAME)) { + this.dnName = (X500Name)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateSubjectName."); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(DN_NAME)) { + return(dnName); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateSubjectName."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(DN_NAME)) { + dnName = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateSubjectName."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(DN_NAME); + + return(elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return(NAME); + } +} diff --git a/pki/base/util/src/netscape/security/x509/CertificateSubjectUniqueIdentity.java b/pki/base/util/src/netscape/security/x509/CertificateSubjectUniqueIdentity.java new file mode 100644 index 00000000..2162eb33 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CertificateSubjectUniqueIdentity.java @@ -0,0 +1,181 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Enumeration; + +import netscape.security.util.*; + +/** + * This class defines the subject/issuer unique identity attribute + * for the Certificate. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.6 + * @see CertAttrSet + */ +public class CertificateSubjectUniqueIdentity implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.subjectID"; + /** + * Sub attributes name for this CertAttrSet. + */ + public static final String NAME = "subjectID"; + public static final String ID = "id"; + + private UniqueIdentity id; + + /** + * Default constructor for the certificate attribute. + * + * @param key the UniqueIdentity + */ + public CertificateSubjectUniqueIdentity(UniqueIdentity id) { + this.id = id; + } + + /** + * Create the object, decoding the values from the passed DER stream. + * + * @param in the DerInputStream to read the UniqueIdentity from. + * @exception IOException on decoding errors. + */ + public CertificateSubjectUniqueIdentity(DerInputStream in) + throws IOException { + id = new UniqueIdentity(in); + } + + /** + * Create the object, decoding the values from the passed stream. + * + * @param in the InputStream to read the UniqueIdentity from. + * @exception IOException on decoding errors. + */ + public CertificateSubjectUniqueIdentity(InputStream in) + throws IOException { + DerValue val = new DerValue(in); + id = new UniqueIdentity(val); + } + + /** + * Create the object, decoding the values from the passed DER value. + * + * @param in the DerValue to read the UniqueIdentity from. + * @exception IOException on decoding errors. + */ + public CertificateSubjectUniqueIdentity(DerValue val) + throws IOException { + id = new UniqueIdentity(val); + } + + /** + * Return the identity as user readable string. + */ + public String toString() { + if (id == null) return ""; + return(id.toString()); + } + + /** + * Decode the identity in DER form from the stream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception IOException on errors. + */ + public void decode(InputStream in) throws IOException { + DerValue val = new DerValue(in); + id = new UniqueIdentity(val); + } + + /** + * Encode the identity in DER form to the stream. + * + * @param out the DerOutputStream to marshal the contents to. + * @exception IOException on errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + id.encode(tmp,DerValue.createTag(DerValue.TAG_CONTEXT,false,(byte)2)); + + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (!(obj instanceof UniqueIdentity)) { + throw new IOException("Attribute must be of type UniqueIdentity."); + } + if (name.equalsIgnoreCase(ID)) { + id = (UniqueIdentity)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet: CertificateSubjectUniqueIdentity."); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(ID)) { + return(id); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet: CertificateSubjectUniqueIdentity."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(ID)) { + id = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet: CertificateSubjectUniqueIdentity."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(ID); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/pki/base/util/src/netscape/security/x509/CertificateValidity.java b/pki/base/util/src/netscape/security/x509/CertificateValidity.java new file mode 100644 index 00000000..ad2865eb --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CertificateValidity.java @@ -0,0 +1,294 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.cert.*; +import java.util.Date; +import java.util.Enumeration; + +import netscape.security.util.*; + +/** + * This class defines the interval for which the certificate is valid. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.12 + * @see CertAttrSet + */ +public class CertificateValidity implements CertAttrSet, Serializable { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.validity"; + /** + * Sub attributes name for this CertAttrSet. + */ + public static final String NAME = "validity"; + public static final String NOT_BEFORE = "notBefore"; + public static final String NOT_AFTER = "notAfter"; + private static final long YR_2050 = 2524636800000L; + + // Private data members + private Date notBefore; + private Date notAfter; + + // Returns the first time the certificate is valid. + private Date getNotBefore() { + return (new Date(notBefore.getTime())); + } + + // Returns the last time the certificate is valid. + private Date getNotAfter() { + return (new Date(notAfter.getTime())); + } + + // Construct the class from the DerValue + private void construct(DerValue derVal) throws IOException { + if (derVal.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoded CertificateValidity, " + + "starting sequence tag missing."); + } + // check if UTCTime encoded or GeneralizedTime + if (derVal.data.available() == 0) + throw new IOException("No data encoded for CertificateValidity"); + + DerInputStream derIn = new DerInputStream(derVal.toByteArray()); + DerValue[] seq = derIn.getSequence(2); + if (seq.length != 2) + throw new IOException("Invalid encoding for CertificateValidity"); + + if (seq[0].tag == DerValue.tag_UtcTime) { + notBefore = derVal.data.getUTCTime(); + } else if (seq[0].tag == DerValue.tag_GeneralizedTime) { + notBefore = derVal.data.getGeneralizedTime(); + } else { + throw new IOException("Invalid encoding for CertificateValidity"); + } + + if (seq[1].tag == DerValue.tag_UtcTime) { + notAfter = derVal.data.getUTCTime(); + } else if (seq[1].tag == DerValue.tag_GeneralizedTime) { + notAfter = derVal.data.getGeneralizedTime(); + } else { + throw new IOException("Invalid encoding for CertificateValidity"); + } + } + + /** + * Default constructor for the class. + */ + public CertificateValidity() { } + + /** + * The default constructor for this class for the specified interval. + * + * @param notBefore the date and time before which the certificate + * is not valid. + * @param notAfter the date and time after which the certificate is + * not valid. + */ + public CertificateValidity(Date notBefore, Date notAfter) { + this.notBefore = notBefore; + this.notAfter = notAfter; + } + + /** + * Create the object, decoding the values from the passed DER stream. + * + * @param in the DerInputStream to read the CertificateValidity from. + * @exception IOException on decoding errors. + */ + public CertificateValidity(DerInputStream in) throws IOException { + DerValue derVal = in.getDerValue(); + construct(derVal); + } + + /** + * Return the validity period as user readable string. + */ + public String toString() { + if (notBefore == null || notAfter == null) + return ""; + return ("Validity: [From: " + notBefore.toString() + + ",\n To: " + notAfter.toString() + "]"); + } + + /** + * Decode the CertificateValidity period from the InputStream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception IOException on errors. + */ + public void decode(InputStream in) throws IOException { + DerValue derVal = new DerValue(in); + construct(derVal); + } + + private synchronized void writeObject(ObjectOutputStream stream) + throws IOException { + encode(stream); + } + + private synchronized void readObject(ObjectInputStream stream) + throws IOException { + decode(stream); + } + + /** + * Encode the CertificateValidity period in DER form to the stream. + * + * @param out the OutputStream to marshal the contents to. + * @exception IOException on errors. + */ + public void encode(OutputStream out) throws IOException { + + // in cases where default constructor is used check for + // null values + if (notBefore == null || notAfter == null) { + throw new IOException("CertAttrSet:CertificateValidity:" + + " null values to encode.\n"); + } + DerOutputStream pair = new DerOutputStream(); + + if (notBefore.getTime() < YR_2050) { + pair.putUTCTime(notBefore); + } else + pair.putGeneralizedTime(notBefore); + + if (notAfter.getTime() < YR_2050) { + pair.putUTCTime(notAfter); + } else { + pair.putGeneralizedTime(notAfter); + } + DerOutputStream seq = new DerOutputStream(); + seq.write(DerValue.tag_Sequence,pair); + + out.write(seq.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (!(obj instanceof Date)) { + throw new IOException("Attribute must be of type Date."); + } + if (name.equalsIgnoreCase(NOT_BEFORE)) { + notBefore = (Date)obj; + } else if (name.equalsIgnoreCase(NOT_AFTER)) { + notAfter = (Date)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet: CertificateValidity."); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(NOT_BEFORE)) { + return (getNotBefore()); + } else if (name.equalsIgnoreCase(NOT_AFTER)) { + return (getNotAfter()); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet: CertificateValidity."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(NOT_BEFORE)) { + notBefore = null; + } else if (name.equalsIgnoreCase(NOT_AFTER)) { + notAfter = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet: CertificateValidity."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(NOT_BEFORE); + elements.addElement(NOT_AFTER); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } + + /** + * Verify that the current time is within the validity period. + * + * @exception CertificateExpiredException if the certificate has expired. + * @exception CertificateNotYetValidException if the certificate is not + * yet valid. + */ + public void valid() + throws CertificateNotYetValidException, CertificateExpiredException { + Date now = new Date(); + valid(now); + } + + /** + * Verify that the passed time is within the validity period. + * @param now the Date against which to compare the validity + * period. + * + * @exception CertificateExpiredException if the certificate has expired + * with respect to the <code>Date</code> supplied. + * @exception CertificateNotYetValidException if the certificate is not + * yet valid with respect to the <code>Date</code> supplied. + * + */ + public void valid(Date now) + throws CertificateNotYetValidException, CertificateExpiredException { + /* + * we use the internal Dates rather than the passed in Date + * because someone could override the Date methods after() + * and before() to do something entirely different. + */ + if (notBefore.after(now)) { + throw new CertificateNotYetValidException("NotBefore: " + + notBefore.toString()); + } + if (notAfter.before(now)) { + throw new CertificateExpiredException("NotAfter: " + + notAfter.toString()); + } + } +} diff --git a/pki/base/util/src/netscape/security/x509/CertificateVersion.java b/pki/base/util/src/netscape/security/x509/CertificateVersion.java new file mode 100644 index 00000000..fee1af65 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CertificateVersion.java @@ -0,0 +1,244 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Date; +import java.util.Enumeration; + +import netscape.security.util.*; + +/** + * This class defines the version of the X509 Certificate. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.8 + * @see CertAttrSet + */ +public class CertificateVersion implements CertAttrSet { + /** + * X509Certificate Version 1 + */ + public static final int V1 = 0; + /** + * X509Certificate Version 2 + */ + public static final int V2 = 1; + /** + * X509Certificate Version 3 + */ + public static final int V3 = 2; + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.version"; + /** + * Sub attributes name for this CertAttrSet. + */ + public static final String NAME = "version"; + public static final String VERSION = "number"; + + // Private data members + int version = V1; + + // Returns the version number. + private int getVersion() { + return(version); + } + + // Construct the class from the passed DerValue + private void construct(DerValue derVal) throws IOException { + if (derVal.isConstructed() && derVal.isContextSpecific()) { + derVal = derVal.data.getDerValue(); + version = derVal.getInteger().toInt(); + if (derVal.data.available() != 0) { + throw new IOException("X.509 version, bad format"); + } + } + } + + /** + * The default constructor for this class, + * sets the version to 0 (i.e. X.509 version 1). + */ + public CertificateVersion() { + version = V1; + } + + /** + * The constructor for this class for the required version. + * + * @param version the version for the certificate. + * @exception IOException if the version is not valid. + */ + public CertificateVersion(int version) throws IOException { + + // check that it is a valid version + if (version == V1 || version == V2 || version == V3) + this.version = version; + else { + throw new IOException("X.509 Certificate version " + + version + " not supported.\n"); + } + } + + /** + * Create the object, decoding the values from the passed DER stream. + * + * @param in the DerInputStream to read the CertificateVersion from. + * @exception IOException on decoding errors. + */ + public CertificateVersion(DerInputStream in) throws IOException { + version = V1; + DerValue derVal = in.getDerValue(); + + construct(derVal); + } + + /** + * Create the object, decoding the values from the passed stream. + * + * @param in the InputStream to read the CertificateVersion from. + * @exception IOException on decoding errors. + */ + public CertificateVersion(InputStream in) throws IOException { + version = V1; + DerValue derVal = new DerValue(in); + + construct(derVal); + } + + /** + * Create the object, decoding the values from the passed DerValue. + * + * @param val the Der encoded value. + * @exception IOException on decoding errors. + */ + public CertificateVersion(DerValue val) throws IOException { + version = V1; + + construct(val); + } + + /** + * Return the version number of the certificate. + */ + public String toString() { + return("Version: V" + (version+1)); + } + + /** + * Encode the CertificateVersion period in DER form to the stream. + * + * @param out the OutputStream to marshal the contents to. + * @exception IOException on errors. + */ + public void encode(OutputStream out) throws IOException { + // Nothing for default + if (version == V1) { + return; + } + DerOutputStream tmp = new DerOutputStream(); + tmp.putInteger(new BigInt(version)); + + DerOutputStream seq = new DerOutputStream(); + seq.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0), + tmp); + + out.write(seq.toByteArray()); + } + + /** + * Decode the CertificateVersion period in DER form from the stream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception IOException on errors. + */ + public void decode(InputStream in) throws IOException { + DerValue derVal = new DerValue(in); + construct(derVal); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (!(obj instanceof Integer)) { + throw new IOException("Attribute must be of type Integer."); + } + if (name.equalsIgnoreCase(VERSION)) { + version = ((Integer)obj).intValue(); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet: CertificateVersion."); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(VERSION)) { + return(Integer.valueOf(getVersion())); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet: CertificateVersion."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(VERSION)) { + version = V1; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet: CertificateVersion."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(VERSION); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return(NAME); + } + + /** + * Compare versions. + */ + public int compare(int vers) { + return(version - vers); + } +} diff --git a/pki/base/util/src/netscape/security/x509/CertificateX509Key.java b/pki/base/util/src/netscape/security/x509/CertificateX509Key.java new file mode 100644 index 00000000..b83dd4f6 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/CertificateX509Key.java @@ -0,0 +1,180 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Enumeration; + +import netscape.security.util.*; + +/** + * This class defines the X509Key attribute for the Certificate. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.5 + * @see CertAttrSet + */ +public class CertificateX509Key implements CertAttrSet, Serializable { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.key"; + /** + * Sub attributes name for this CertAttrSet. + */ + public static final String NAME = "key"; + public static final String KEY = "value"; + + // Private data member + private X509Key key; + + /** + * Default constructor for the certificate attribute. + * + * @param key the X509Key + */ + public CertificateX509Key(X509Key key) { + this.key = key; + } + + /** + * Create the object, decoding the values from the passed DER stream. + * + * @param in the DerInputStream to read the X509Key from. + * @exception IOException on decoding errors. + */ + public CertificateX509Key(DerInputStream in) throws IOException { + DerValue val = in.getDerValue(); + key = X509Key.parse(val); + } + + /** + * Create the object, decoding the values from the passed stream. + * + * @param in the InputStream to read the X509Key from. + * @exception IOException on decoding errors. + */ + public CertificateX509Key(InputStream in) throws IOException { + DerValue val = new DerValue(in); + key = X509Key.parse(val); + } + + /** + * Return the key as printable string. + */ + public String toString() { + if (key == null) return ""; + return(key.toString()); + } + + /** + * Decode the key in DER form from the stream. + * + * @param in the InputStream to unmarshal the contents from + * @exception IOException on decoding or validity errors. + */ + public void decode(InputStream in) throws IOException { + DerValue val = new DerValue(in); + key = X509Key.parse(val); + } + + private synchronized void writeObject(ObjectOutputStream stream) + throws IOException { + encode(stream); + } + + private synchronized void readObject(ObjectInputStream stream) + throws IOException { + decode(stream); + } + + /** + * Encode the key in DER form to the stream. + * + * @param out the OutputStream to marshal the contents to. + * @exception IOException on errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + key.encode(tmp); + + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (!(obj instanceof X509Key)) { + throw new IOException("Attribute must be of type X509Key."); + } + if (name.equalsIgnoreCase(KEY)) { + this.key = (X509Key)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet: CertificateX509Key."); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(KEY)) { + return(key); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet: CertificateX509Key."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(KEY)) { + key = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet: CertificateX509Key."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(KEY); + + return(elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return(NAME); + } +} diff --git a/pki/base/util/src/netscape/security/x509/DNSName.java b/pki/base/util/src/netscape/security/x509/DNSName.java new file mode 100644 index 00000000..7e503b4a --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/DNSName.java @@ -0,0 +1,77 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; + +import netscape.security.util.*; + +/** + * This class implements the DNSName as required by the GeneralNames + * ASN.1 object. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.4 + */ +public class DNSName implements GeneralNameInterface { + private String name; + + /** + * Create the DNSName object from the passed encoded Der value. + * + * @param derValue the encoded DER DNSName. + * @exception IOException on error. + */ + public DNSName(DerValue derValue) throws IOException { + name = derValue.getIA5String(); + } + + /** + * Create the DNSName object with the specified name. + * + * @param name the DNSName. + */ + public DNSName(String name) { + this.name = name; + } + + /** + * Return the type of the GeneralName. + */ + public int getType() { + return (GeneralNameInterface.NAME_DNS); + } + + /** + * Encode the DNS name into the DerOutputStream. + * + * @param out the DER stream to encode the DNSName to. + * @exception IOException on encoding errors. + */ + public void encode(DerOutputStream out) throws IOException { + out.putIA5String(name); + } + + /** + * Convert the name into user readable string. + */ + public String toString() { + return ("DNSName: " + name); + } +} diff --git a/pki/base/util/src/netscape/security/x509/DeltaCRLIndicatorExtension.java b/pki/base/util/src/netscape/security/x509/DeltaCRLIndicatorExtension.java new file mode 100755 index 00000000..39664e23 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/DeltaCRLIndicatorExtension.java @@ -0,0 +1,232 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.math.BigInteger; +import java.util.Enumeration; +import java.security.cert.CertificateException; + +import netscape.security.util.*; + +/** + * Represent the Delta CRL Indicator Extension. + * + * <p>The delta CRL indicator is a critical CRL extension + * that identifies a delta-CRL. The value of BaseCRLNumber + * identifies the CRL number of the base CRL that was used + * as the starting point in the generation of this delta- CRL. + * The delta-CRL contains the changes between the base CRL + * and the current CRL issued along with the delta-CRL. + * + * @see Extension + * @see CertAttrSet + */ +public class DeltaCRLIndicatorExtension extends Extension +implements CertAttrSet { + + /** + * Attribute name. + */ + public static final String NAME = "DeltaCRLIndicator"; + public static final String NUMBER = "value"; + + /** + * The Object Identifier for this extension. + */ + public static final String OID = "2.5.29.27"; + + private BigInt baseCRLNumber = null; + + static { + try { + OIDMap.addAttribute(DeltaCRLIndicatorExtension.class.getName(), + OID, NAME); + } catch (CertificateException e) {} + } + + // Encode this extension value + private void encodeThis() throws IOException { + if (baseCRLNumber == null) + throw new IOException("Unintialized delta CRL indicator extension"); + DerOutputStream os = new DerOutputStream(); + os.putInteger(this.baseCRLNumber); + this.extensionValue = os.toByteArray(); + } + + /** + * Create a DeltaCRLIndicatorExtension with the integer value. + * The criticality is set to true. + * + * @param baseCRLNum the value to be set for the extension. + */ + public DeltaCRLIndicatorExtension(int baseCRLNum) throws IOException { + this.baseCRLNumber = new BigInt(baseCRLNum); + this.extensionId = PKIXExtensions.DeltaCRLIndicator_Id; + this.critical = true; + encodeThis(); + } + + /** + * Create a DeltaCRLIndicatorExtension with the BigInteger value. + * The criticality is set to true. + * + * @param baseCRLNum the value to be set for the extension. + */ + public DeltaCRLIndicatorExtension(BigInteger baseCRLNum) throws IOException { + this.baseCRLNumber = new BigInt(baseCRLNum); + this.extensionId = PKIXExtensions.DeltaCRLIndicator_Id; + this.critical = true; + encodeThis(); + } + + /** + * Create a DeltaCRLIndicatorExtension with the BigInteger value. + * + * @param critical true if the extension is to be treated as critical. + * @param baseCRLNum the value to be set for the extension. + */ + public DeltaCRLIndicatorExtension(Boolean critical, BigInteger baseCRLNum) + throws IOException { + this.baseCRLNumber = new BigInt(baseCRLNum); + this.extensionId = PKIXExtensions.DeltaCRLIndicator_Id; + this.critical = critical.booleanValue(); + encodeThis(); + } + + /** + * Create the extension from the passed DER encoded value of the same. + * + * @param critical true if the extension is to be treated as critical. + * @param value Array of DER encoded bytes of the actual value. + * @exception IOException on error. + */ + public DeltaCRLIndicatorExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.DeltaCRLIndicator_Id; + this.critical = critical.booleanValue(); + + int len = Array.getLength(value); + byte[] extValue = new byte[len]; + for (int i = 0; i < len; i++) { + extValue[i] = Array.getByte(value, i); + } + this.extensionValue = extValue; + DerValue val = new DerValue(extValue); + this.baseCRLNumber = val.getInteger(); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (name.equalsIgnoreCase(NUMBER)) { + if (!(obj instanceof BigInteger)) { + throw new IOException("Attribute must be of type BigInteger."); + } + baseCRLNumber = new BigInt((BigInteger)obj); + } else { + throw new IOException("Attribute name not recognized by"+ + " CertAttrSet:DeltaCRLIndicator."); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(NUMBER)) { + if (baseCRLNumber == null) return null; + else return baseCRLNumber.toBigInteger(); + } else { + throw new IOException("Attribute name not recognized by"+ + " CertAttrSet:DeltaCRLIndicator."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(NUMBER)) { + baseCRLNumber = null; + } else { + throw new IOException("Attribute name not recognized by"+ + " CertAttrSet:DeltaCRLIndicator."); + } + } + + /** + * Returns a printable representation of the DeltaCRLIndicatorExtension. + */ + public String toString() { + String s = super.toString() + "Delta CRL Indicator: " + + ((baseCRLNumber == null) ? "": baseCRLNumber.toString()) + + "\n"; + return (s); + } + + /** + * Decode the extension from the InputStream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception IOException on decoding or validity errors. + */ + public void decode(InputStream in) throws IOException { + throw new IOException("Method not to be called directly."); + } + + /** + * Write the extension to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + + if (this.extensionValue == null) { + this.extensionId = PKIXExtensions.DeltaCRLIndicator_Id; + this.critical = true; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements () { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(NUMBER); + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName () { + return (NAME); + } +} + diff --git a/pki/base/util/src/netscape/security/x509/DirStrConverter.java b/pki/base/util/src/netscape/security/x509/DirStrConverter.java new file mode 100644 index 00000000..23dea47b --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/DirStrConverter.java @@ -0,0 +1,196 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.*; +import sun.io.CharToByteConverter; +import netscape.security.util.*; + +/** + * A DirStrConverter converts a string to a DerValue of ASN.1 Directory String, + * which is a CHOICE of Printable (subset of ASCII), T.61 (Teletex) or + * Universal String (UCS-4), and vice versa. + * + * <p>The string to DerValue conversion is done as follows. + * If the string has only PrintableString characters it is converted + * to a ASN.1 Printable String using the PrintableString + * CharToByteConverter from the global default ASN1CharStrConvMap. + * If it has only characters covered in the PrintableString or T.61 + * character set it is converted to a ASN.1 T.61 string using the T.61 + * CharToByteConverter from the ASN1CharStrCovnMap. + * Otherwise it is converted to a ASN.1 UniversalString (UCS-4 character set) + * which covers all characters. + * + * @see AVAValueConverter + * @see ASN1CharStrConvMap + * + * @author Lily Hsiao, Slava Galperin at Netscape Communications, Inc. + */ + + +public class DirStrConverter implements AVAValueConverter +{ + // public constructors + + /** + * Constructs a DirStrConverter. + */ + public DirStrConverter() + { + } + + // public functions + + /** + * Converts a string to a DER encoded ASN1 Directory String, which is a + * CHOICE of PrintableString, T.61String or UniversalString. + * The string is taken as is i.e. should not be in Ldap DN string syntax. + * + * @param ds a string representing a directory string value. + * + * @return a DerValue + * + * @exception IOException if the string cannot be converted, such as + * when a UniversalString CharToByteConverter + * isn't available and the string contains + * characters covered only in the universal + * string (or UCS-4) character set. + */ + private static byte[] DefEncodingOrder = + new byte[] { + DerValue.tag_PrintableString, + DerValue.tag_T61String, + DerValue.tag_UniversalString + }; + + public static synchronized void + setDefEncodingOrder(byte[] defEncodingOrder) + { + DefEncodingOrder = defEncodingOrder; + } + + public DerValue getValue(String ds) + throws IOException + { + return getValue(ds, DefEncodingOrder); + } + + /** + * Like getValue(String) with specified DER tags as encoding order. + */ + public DerValue getValue(String ds, byte[] tags) + throws IOException + { + // try to convert to printable, then t61 the universal - + // i.e. from minimal to the most liberal. + + int ret = -1; + CharToByteConverter cbc; + DerValue value; + byte[] bbuf, derBuf; + int i; + + if (tags == null || tags.length == 0) + tags = DefEncodingOrder; + + bbuf = new byte[4*ds.length()]; + for (i = 0; i < tags.length; i++) + { + try { + cbc = ASN1CharStrConvMap.getDefault().getCBC(tags[i]); + if (cbc == null) + continue; + ret = cbc.convert(ds.toCharArray(), 0, ds.length(), + bbuf, 0, bbuf.length); + break; + } + catch (java.io.CharConversionException e) { + continue; + } + catch (InstantiationException e) { + throw new IOException("Cannot instantiate CharToByteConverter"); + } + catch (IllegalAccessException e) { + throw new IOException( + "Illegal Access loading CharToByteConverter"); + } + } + if (ret == -1) { + throw new IOException( + "Cannot convert the directory string value to a ASN.1 type"); + } + + derBuf = new byte[ret]; + System.arraycopy(bbuf, 0, derBuf, 0, ret); + return new DerValue(tags[i], derBuf); + } + + /** + * Creates a DerValue from a BER encoded value, obtained from for example + * a attribute value in octothorpe form of a Ldap DN string. + * Checks if the BER encoded value is legal for a DirectoryString. + * + * NOTE: currently only supports DER encoding for the BER encoded value. + * + * @param berStream Byte array of a BER encoded value. + * + * @return DerValue object. + * + * @exception IOException If the BER value cannot be converted to a + * valid Directory String DER value. + */ + public DerValue getValue(byte[] berByteStream) + throws IOException + { + DerValue value = new DerValue(berByteStream); + + /* + if (value.tag != DerValue.tag_PrintableString && + value.tag != DerValue.tag_T61String && + value.tag != DerValue.tag_UniversalString) + throw new IOException("Invalid Directory String AVA Value"); + */ + + return value; + } + + /** + * Converts a DerValue to a string. + * The string is not in any syntax, such as RFC1779 string syntax. + * + * @param avaValue a DerValue + * @return a string if the value can be converted. + * @exception IOException if a ByteToCharConverter needed for the + * conversion is not available. + */ + public String getAsString(DerValue avaValue) + throws IOException + { + /* + if (avaValue.tag != DerValue.tag_PrintableString && + avaValue.tag != DerValue.tag_BMPString && + avaValue.tag != DerValue.tag_UniversalString && + avaValue.tag != DerValue.tag_T61String) + throw new IllegalArgumentException( + "Invalid Directory String value"); + // NOTE will return null if a ByteToCharConverter is not available. + */ + return avaValue.getASN1CharString(); + } + +} diff --git a/pki/base/util/src/netscape/security/x509/DisplayText.java b/pki/base/util/src/netscape/security/x509/DisplayText.java new file mode 100644 index 00000000..c388b498 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/DisplayText.java @@ -0,0 +1,69 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import netscape.security.util.*; + + +/** + * Represent the DisplayText. + * + * DisplayText ::= CHOICE { + * visibleString VisibleString (SIZE (1..200)), + * bmpString BMPString (SIZE (1..200)), + * utf8String UTF8String (SIZE (1..200)), + * } + * + * @author Thomas Kwan + */ +public class DisplayText { + + /** Tag value indicating an ASN.1 "BMPString" value. */ + public final static byte tag_IA5String = 0x16; + public final static byte tag_BMPString = 0x1E; + public final static byte tag_VisibleString = 0x1A; + public final static byte tag_UTF8String = 0x0C; + + private byte mTag; + private String mS = null; + + public DisplayText(byte tag, String s) { + mTag = tag; + mS = s; + } + + public DisplayText(DerValue val) throws IOException { + mTag = val.tag; + mS = val.getAsString(); + } + + /** + * Write the DisplayText to the DerOutputStream. + * + * @param out the DerOutputStream to write the object to. + * @exception IOException on errors. + */ + public void encode(DerOutputStream out) throws IOException { + out.putStringType(mTag, mS); + } + + public String getText() { + return mS; + } +} diff --git a/pki/base/util/src/netscape/security/x509/EDIPartyName.java b/pki/base/util/src/netscape/security/x509/EDIPartyName.java new file mode 100644 index 00000000..45510e28 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/EDIPartyName.java @@ -0,0 +1,146 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import netscape.security.util.*; + +/** + * This class defines the EDIPartyName of the GeneralName choice. + * The ASN.1 syntax for this is: + * <pre> + * EDIPartyName ::= SEQUENCE { + * nameAssigner [0] DirectoryString OPTIONAL, + * partyName [1] DirectoryString } + * </pre> + * + * @author Hemma Prafullchandra + * @version 1.2 + * @see GeneralName + * @see GeneralNames + * @see GeneralNameInterface + */ +public class EDIPartyName implements GeneralNameInterface { + + // Private data members + private static final byte TAG_ASSIGNER = 0; + private static final byte TAG_PARTYNAME = 1; + + private String assigner = null; + private String party = null; + + /** + * Create the EDIPartyName object from the specified names. + * + * @param assignerName the name of the assigner + * @param partyName the name of the EDI party. + */ + public EDIPartyName(String assignerName, String partyName) { + this.assigner = assignerName; + this.party = partyName; + } + + /** + * Create the EDIPartyName object from the specified name. + * + * @param partyName the name of the EDI party. + */ + public EDIPartyName(String partyName) { + this.party = partyName; + } + + /** + * Create the EDIPartyName object from the passed encoded Der value. + * + * @param derValue the encoded DER EDIPartyName. + * @exception IOException on error. + */ + public EDIPartyName(DerValue derValue) throws IOException { + DerInputStream in = new DerInputStream(derValue.toByteArray()); + DerValue[] seq = in.getSequence(2); + + int len = seq.length; + if (len < 1 || len > 2) + throw new IOException("Invalid encoding of EDIPartyName"); + + for (int i = 0; i < len; i++) { + DerValue opt = seq[i]; + if (opt.isContextSpecific((byte)TAG_ASSIGNER) && + !opt.isConstructed()) { + if (assigner != null) + throw new IOException("Duplicate nameAssigner found in" + + " EDIPartyName"); + opt = opt.data.getDerValue(); + assigner = opt.getAsString(); + } + if (opt.isContextSpecific((byte)TAG_PARTYNAME) && + !opt.isConstructed()) { + if (party != null) + throw new IOException("Duplicate partyName found in" + + " EDIPartyName"); + opt = opt.data.getDerValue(); + party = opt.getAsString(); + } + } + } + + /** + * Return the type of the GeneralName. + */ + public int getType() { + return (GeneralNameInterface.NAME_EDI); + } + + /** + * Encode the EDI party name into the DerOutputStream. + * + * @param out the DER stream to encode the EDIPartyName to. + * @exception IOException on encoding errors. + */ + public void encode(DerOutputStream out) throws IOException { + DerOutputStream tagged = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + + if (assigner != null) { + DerOutputStream tmp2 = new DerOutputStream(); + // XXX - shd check is chars fit into PrintableString + tmp2.putPrintableString(assigner); + tagged.write(DerValue.createTag(DerValue.TAG_CONTEXT, + false, TAG_ASSIGNER), tmp2); + } + if (party == null) + throw new IOException("Cannot have null partyName"); + + // XXX - shd check is chars fit into PrintableString + tmp.putPrintableString(party); + tagged.write(DerValue.createTag(DerValue.TAG_CONTEXT, + false, TAG_PARTYNAME), tmp); + + out.write(DerValue.tag_Sequence, tagged); + } + + /** + * Return the printable string. + */ + public String toString() { + return ("EDIPartyName: " + + ((assigner == null) ? "" : + (" nameAssigner = " + assigner + ",")) + + " partyName = " + party); + } +} diff --git a/pki/base/util/src/netscape/security/x509/Extension.java b/pki/base/util/src/netscape/security/x509/Extension.java new file mode 100644 index 00000000..7a4f6d12 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/Extension.java @@ -0,0 +1,191 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.Serializable; +import java.security.cert.CertificateException; +import netscape.security.util.*; + + +/** + * Represent a X509 Extension Attribute. + * + * <p>Extensions are addiitonal attributes which can be inserted in a X509 + * v3 certificate. For example a "Driving License Certificate" could have + * the driving license number as a extension. + * + * <p>Extensions are represented as a sequence of the extension identifier + * (Object Identifier), a boolean flag stating whether the extension is to + * be treated as being critical and the extension value itself (this is again + * a DER encoding of the extension value). + * <pre> + * ASN.1 definition of Extension: + * Extension ::= SEQUENCE { + * ExtensionId OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * extensionValue OCTET STRING + * } + * </pre> + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.9 + */ +public class Extension implements Serializable { + protected ObjectIdentifier extensionId = null; + protected boolean critical = false; + protected byte[] extensionValue = null; + + /** + * Default constructor. Used only by sub-classes. + */ + public Extension() { } + + /** + * Constructs an extension from a DER encoded array of bytes. + */ + public Extension(DerValue derVal) throws IOException { + + DerInputStream in = derVal.toDerInputStream(); + + // Object identifier + extensionId = in.getOID(); + + // If the criticality flag was false, it will not have been encoded. + DerValue val = in.getDerValue(); + if (val.tag == DerValue.tag_Boolean) { + critical = val.getBoolean(); + + // Extension value (DER encoded) + val = in.getDerValue(); + extensionValue = val.getOctetString(); + } else { + critical = false; + extensionValue = val.getOctetString(); + } + } + + /** + * Constructs an Extension from individual components of ObjectIdentifier, + * criticality and the DER encoded OctetString. + * + * @param extensionId the ObjectIdentifier of the extension + * @param critical the boolean indicating if the extension is critical + * @param extensionValue the DER encoded octet string of the value. + */ + public Extension(ObjectIdentifier extensionId, boolean critical, + byte[] extensionValue) throws IOException { + this.extensionId = extensionId; + this.critical = critical; + // passed in a DER encoded octet string, strip off the tag + // and length + DerValue inDerVal = new DerValue(extensionValue); + this.extensionValue = inDerVal.getOctetString(); + } + + /** + * Constructs an Extension from another extension. To be used for + * creating decoded subclasses. + * + * @param ext the extension to create from. + */ + public Extension(Extension ext) { + this.extensionId = ext.extensionId; + this.critical = ext.critical; + this.extensionValue = ext.extensionValue; + } + + /** + * Write the extension to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to. + * @exception IOException on encoding errors + */ + public void encode(DerOutputStream out) throws IOException { + DerOutputStream bytes = new DerOutputStream(); + + if (extensionId == null) + throw new IOException("Null OID to encode for the extension!"); + + bytes.putOID(extensionId); + if (critical) + bytes.putBoolean(critical); + if (extensionValue != null) + bytes.putOctetString(extensionValue); + + out.write(DerValue.tag_Sequence, bytes); + } + + /** + * Returns true if extension is critical. + */ + public boolean isCritical() { + return (critical); + } + + public void setCritical(boolean c) { + critical = c; + } + + public void clearValue() { + extensionValue = null; + } + + /** + * Returns the ObjectIdentifier of the extension. + */ + public ObjectIdentifier getExtensionId() { + return (extensionId); + } + + public void setExtensionId(ObjectIdentifier oid) { + extensionId = oid; + } + + /** + * Returns the extension value as an byte array for further processing. + * Note, this is the raw DER value of the extension, not the DER + * encoded octet string which is in the certificate. + */ + public byte[] getExtensionValue() { + if (extensionValue == null) + return null; + + byte[] dup = new byte[extensionValue.length]; + System.arraycopy(extensionValue, 0, dup, 0, dup.length); + return (dup); + } + + public void setExtensionValue(byte value[]) { + extensionValue = value; + } + + /** + * Returns the Extension in user readable form. + */ + public String toString() { + String s = "ObjectId: " + extensionId.toString(); + if (critical) { + s += " Criticality=true\n"; + } else { + s += " Criticality=false\n"; + } + return (s); + } +} diff --git a/pki/base/util/src/netscape/security/x509/Extensions.java b/pki/base/util/src/netscape/security/x509/Extensions.java new file mode 100644 index 00000000..75bb6c97 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/Extensions.java @@ -0,0 +1,217 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.security.cert.CertificateException; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import netscape.security.util.*; + +/** + * This class defines the Extensions attribute for the Certificate. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.11 + * @see CertAttrSet + */ +public class Extensions extends Vector +implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.extensions"; + /** + * name + */ + public static final String NAME = "extensions"; + + private Hashtable map; + + // Parse the encoded extension + public void parseExtension(Extension ext) throws IOException { + try { + Class extClass = OIDMap.getClass(ext.getExtensionId()); + if (extClass == null) { // Unsupported extension + if (ext.isCritical()) { + throw new IOException("Unsupported CRITICAL extension: " + + ext.getExtensionId()); + } else { + map.put(ext.getExtensionId().toString(), ext); + addElement(ext); + return; + } + } + Class[] params = { Boolean.class, Object.class }; + Constructor cons = extClass.getConstructor(params); + + byte[] extData = ext.getExtensionValue(); + int extLen = extData.length; + Object value = Array.newInstance(byte.class, extLen); + + for (int i = 0; i < extLen; i++) { + Array.setByte(value, i, extData[i]); + } + Object[] passed = new Object[] {new Boolean(ext.isCritical()), + value}; + CertAttrSet certExt = (CertAttrSet)cons.newInstance(passed); + map.put(certExt.getName(), certExt); + addElement(certExt); + + } catch (NoSuchMethodException nosuch) { + throw new IOException(nosuch.toString()); + } catch (InvocationTargetException invk) { + throw new IOException(invk.getTargetException().toString()); + } catch (Exception e) { + throw new IOException(e.toString()); + } + } + + /** + * Default constructor for the certificate attribute. + */ + public Extensions() { + map = new Hashtable(); + } + + /** + * Create the object, decoding the values from the passed DER stream. + * + * @param in the DerInputStream to read the Extension from. + * @exception IOException on decoding errors. + */ + public Extensions(DerInputStream in) + throws IOException { + + map = new Hashtable(); + DerValue[] exts = in.getSequence(5); + + for (int i = 0; i < exts.length; i++) { + Extension ext = new Extension(exts[i]); + parseExtension(ext); + } + } + + /** + * Decode the extensions from the InputStream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception IOException on decoding or validity errors. + */ + public void decode(InputStream in) throws IOException { + DerValue val = new DerValue(in); + DerInputStream str = val.toDerInputStream(); + + map = new Hashtable(); + DerValue[] exts = str.getSequence(5); + + for (int i = 0; i < exts.length; i++) { + Extension ext = new Extension(exts[i]); + parseExtension(ext); + } + } + + /** + * Encode the extensions in DER form to the stream. + * + * @param out the DerOutputStream to marshal the contents to. + * @exception CertificateException on encoding errors. + * @exception IOException on errors. + */ + public void encode(OutputStream out) + throws CertificateException, IOException { + DerOutputStream extOut = new DerOutputStream(); + for (int i = 0; i < size(); i++) { + Object thisOne = elementAt(i); + if (thisOne instanceof CertAttrSet) + ((CertAttrSet)thisOne).encode(extOut); + else if (thisOne instanceof Extension) + ((Extension)thisOne).encode(extOut); + else + throw new CertificateException("Invalid extension object"); + } + + DerOutputStream seq = new DerOutputStream(); + seq.write(DerValue.tag_Sequence,extOut); + + + out.write(seq.toByteArray()); + } + + /** + * Set the attribute value. + * @param name the extension name used in the cache. + * @param obj the object to set. + * @exception IOException if the object could not be cached. + */ + public void set(String name, Object obj) throws IOException { + map.put(name,obj); + addElement(obj); + } + + /** + * Get the attribute value. + * @param name the extension name used in the lookup. + * @exception IOException if named extension is not found. + */ + public Object get(String name) throws IOException { + Object obj = map.get(name); + if (obj == null) { + throw new IOException("No extension found with name " + name); + } + return (obj); + } + + /** + * Delete the attribute value. + * @param name the extension name used in the lookup. + * @exception IOException if named extension is not found. + */ + public void delete(String name) throws IOException { + Object obj = map.get(name); + if (obj == null) { + throw new IOException("No extension found with name " + name); + } + map.remove(name); + removeElement(obj); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements () { + return (map.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName () { + return (NAME); + } +} diff --git a/pki/base/util/src/netscape/security/x509/FreshestCRLExtension.java b/pki/base/util/src/netscape/security/x509/FreshestCRLExtension.java new file mode 100644 index 00000000..cfc75bfd --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/FreshestCRLExtension.java @@ -0,0 +1,386 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import netscape.security.util.*; +import netscape.security.x509.*; +import java.io.*; +import org.mozilla.jss.asn1.*; +import java.security.cert.*; +import java.util.Vector; +import java.util.Enumeration; +import java.util.Hashtable; + +/** + * An extension that tells applications where to find + * the latest (freshest) delta CRL for this certificate + * or full CRL. + * + * <pre> + * cRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint + * + * DistributionPoint ::= SEQUENCE { + * distributionPoint [0] DistributionPointName OPTIONAL, + * reasons [1] ReasonFlags OPTIONAL, + * cRLIssuer [2] GeneralNames OPTIONAL } + * + * DistributionPointName ::= CHOICE { + * fullName [0] GeneralNames, + * nameRelativeToCRLIssuer [1] RelativeDistinguishedName } + * + * ReasonFlags ::= BIT STRING { + * unused (0), + * keyCompromise (1), + * cACompromise (2), + * affiliationChanged (3), + * superseded (4), + * cessationOfOperation (5), + * certificateHold (6) } + * </pre> + */ +public class FreshestCRLExtension extends Extension + implements CertAttrSet +{ + + // vector of CRLDistributionPoint + private SEQUENCE distributionPoints = new SEQUENCE(); + + public FreshestCRLExtension() { + this.extensionId = PKIXExtensions.FreshestCRL_Id; + this.critical = false; + } + + // Cached DER-encoding to improve performance. + private byte[] cachedEncoding=null; + + // Attribute name + public static final String NAME = "FreshestCRL"; + + // The Object Identifier for this extension. + public static final String OID = "2.5.29.46"; + + static { + try { + OIDMap.addAttribute(FreshestCRLExtension.class.getName(), + OID, NAME); + } catch (CertificateException e) {} + } + + /** + * This constructor is called by the CertificateExtensions class to decode + * an extension whose OID indicates it is a CRLDistributionsPoints + * extension. + */ + public FreshestCRLExtension(Boolean critical, Object value) + //throws IOException + { + try { + this.extensionId = PKIXExtensions.FreshestCRL_Id; + this.critical = critical.booleanValue(); + this.extensionValue = (byte[])((byte[])value).clone(); + + // decode the value + try { + SEQUENCE.OF_Template seqOfCRLDP = + new SEQUENCE.OF_Template( CRLDistributionPoint.getTemplate() ); + + distributionPoints = + (SEQUENCE) ASN1Util.decode( seqOfCRLDP, extensionValue ); + } catch(InvalidBERException e) { + throw new IOException("Invalid BER-encoding: " + e.toString()); + } + } catch(IOException e) { + System.out.println("Big error"); + System.out.println(e); + e.printStackTrace(); + //throw e; + } + } + + /** + * Creates a new FreshestCRL extension, with the given + * distribution point as the first element. + */ + public FreshestCRLExtension(CRLDistributionPoint dp) { + this.extensionId = PKIXExtensions.FreshestCRL_Id; + this.critical = false; + distributionPoints.addElement(dp); + } + + /** + * Adds an additional distribution point to the end of the sequence. + */ + public void addPoint(CRLDistributionPoint dp) { + distributionPoints.addElement(dp); + cachedEncoding = null; + } + + /** + * Returns the number of distribution points in the sequence. + */ + public int getNumPoints() { + return distributionPoints.size(); + } + + /** + * Returns the DistributionPoint at the given index in the sequence. + */ + public CRLDistributionPoint getPointAt(int index) { + return (CRLDistributionPoint) distributionPoints.elementAt(index); + } + + /** + * Sets the criticality of this extension. PKIX dictates that this + * extension SHOULD NOT be critical, so applications can make it critical + * if they have a very good reason. By default, the extension is not + * critical. + */ + public void setCritical(boolean critical) { + this.critical = critical; + } + + /** + * Encodes this extension to the given DerOutputStream. + * This method re-encodes each time it is called, so it is not very + * efficient. + */ + public void encode(DerOutputStream out) throws IOException { + extensionValue = ASN1Util.encode(distributionPoints); + super.encode(out); + } + + /** + * Should be called if any change is made to this data structure + * so that the cached DER encoding can be discarded. + */ + public void flushCachedEncoding() { + cachedEncoding = null; + } + + ///////////////////////////////////////////////////////////// + // CertAttrSet interface + // This interface is not really appropriate for this extension + // because it is so complicated. Therefore, we only provide a + // minimal implementation. + ///////////////////////////////////////////////////////////// + public String toString() { + return NAME; + } + + /** + * DER-encodes this extension to the given OutputStream. + */ + public void encode(OutputStream ostream) + throws CertificateException, IOException + { + if( cachedEncoding == null ) { + // only re-encode if necessary + DerOutputStream tmp = new DerOutputStream(); + encode(tmp); + cachedEncoding = tmp.toByteArray(); + } + ostream.write(cachedEncoding); + } + + public void decode(InputStream in) + throws CertificateException, IOException + { + throw new IOException("Not supported"); + } + + public void set(String name, Object obj) + throws CertificateException, IOException + { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:FreshestCRLExtension"); + } + + public Object get(String name) + throws CertificateException, IOException + { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:FreshestCRLExtension"); + } + + public void delete(String name) + throws CertificateException, IOException + { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:FreshestCRLExtension"); + } + + public Enumeration getElements() { + return (new Vector()).elements(); + } + + + public String getName() { + return NAME; + } + + /** + * Test driver. + */ + public static void main(String args[]) { + + try { + + if( args.length != 1 ) { + System.out.println("Usage: FreshestCRLExtentions "+ + "<outfile>"); + System.exit(-1); + } + + BufferedOutputStream bos = new BufferedOutputStream( + new FileOutputStream(args[0]) ); + + + // URI only + CRLDistributionPoint cdp = new CRLDistributionPoint(); + URIName uri = new URIName("http://www.mycrl.com/go/here"); + GeneralNames generalNames = new GeneralNames(); + generalNames.addElement(uri); + cdp.setFullName(generalNames); + FreshestCRLExtension crldpExt = + new FreshestCRLExtension(cdp); + + // DN only + cdp = new CRLDistributionPoint(); + X500Name dn = new X500Name("CN=Otis Smith,E=otis@fedoraproject.org"+ + ",OU=Certificate Server,O=Fedora,C=US"); + generalNames = new GeneralNames(); + generalNames.addElement(dn); + cdp.setFullName(generalNames); + crldpExt.addPoint(cdp); + + // DN + reason + BitArray ba = new BitArray(5, new byte[] {(byte)0x28} ); + cdp = new CRLDistributionPoint(); + cdp.setFullName(generalNames); + cdp.setReasons(ba); + crldpExt.addPoint(cdp); + + + // relative DN + reason + crlIssuer + cdp = new CRLDistributionPoint(); + RDN rdn = new RDN("OU=foobar dept"); + cdp.setRelativeName(rdn); + cdp.setReasons(ba); + cdp.setCRLIssuer(generalNames); + crldpExt.addPoint(cdp); + + crldpExt.setCritical(true); + crldpExt.encode(bos); + + bos.close(); + + } catch(Exception e) { + e.printStackTrace(); + } + } + + /** + * Represents a reason that a cert may be revoked. These reasons are + * expressed in a ReasonFlags bit string. + */ + public static class Reason { + + private String name; + private byte bitMask; + + private Reason() { } + private Reason(String name, byte bitMask) { + this.name = name; + this.bitMask = bitMask; + map.put(name, this); + list.addElement(this); + } + + private static Hashtable map = new Hashtable(); + private static Vector list = new Vector(); + + public static Reason fromString(String name) { + return (Reason) map.get(name); + } + + public String getName() { + return name; + } + + public byte getBitMask() { + return bitMask; + } + + /** + * Given a bit array representing reason flags, extracts the reasons + * and returns them as an array. + * + * @param bitFlags A bit vector containing reason flags. + * @return An array of reasons contained in the bit vector. + * May be zero-length but will not be null. + */ + public static Reason[] bitArrayToReasonArray(byte bitFlags) { + return bitArrayToReasonArray( new byte[] { bitFlags } ); + } + + /** + * Given a bit array representing reason flags, extracts the reasons + * and returns them as an array. Currently, only the first byte + * of the bitflags are examined. + * + * @param bitFlags A bit vector containing reason flags. The format + * is big-endian (MSB first). Only the first byte is examined. + * @return An array of reasons contained in the bit vector. + * May be zero-length but will not be null. + */ + public static Reason[] bitArrayToReasonArray(byte[] bitFlags) { + byte first = bitFlags[0]; + int size = list.size(); + Vector result = new Vector(); + for(int i = 0; i < size; i++) { + Reason r = (Reason) list.elementAt(i); + byte b = r.getBitMask(); + if( (first & b) != 0 ) { + result.addElement(r); + } + } + size = result.size(); + Reason[] retval = new Reason[size]; + for(int i=0; i < size; i++) { + retval[i] = (Reason) result.elementAt(i); + } + return retval; + } + + public static final Reason UNUSED = + new Reason("unused", (byte) 0x80); + public static final Reason KEY_COMPROMISE = + new Reason("keyCompromise", (byte) 0x40); + public static final Reason CA_COMPROMISE = + new Reason("cACompromise", (byte) 0x20); + public static final Reason AFFILIATION_CHANGED = + new Reason("affiliationChanged", (byte) 0x10); + public static final Reason SUPERSEDED = + new Reason("superseded", (byte) 0x08); + public static final Reason CESSATION_OF_OPERATION = + new Reason("cessationOfOperation", (byte) 0x04); + public static final Reason CERTIFICATE_HOLD = + new Reason("certificateHold", (byte) 0x02); + } + +} diff --git a/pki/base/util/src/netscape/security/x509/GeneralName.java b/pki/base/util/src/netscape/security/x509/GeneralName.java new file mode 100644 index 00000000..9c84e9e4 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/GeneralName.java @@ -0,0 +1,192 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; + +import netscape.security.util.*; + +/** + * This class implements the ASN.1 GeneralName object class. + * <p> + * The ASN.1 syntax for this is: + * <pre> + * GeneralName ::= CHOICE { + * otherName [0] OtherName, + * rfc822Name [1] IA5String, + * dNSName [2] IA5String, + * x400Address [3] ORAddress, + * directoryName [4] Name, + * ediPartyName [5] EDIPartyName, + * uniformResourceIdentifier [6] IA5String, + * iPAddress [7] OCTET STRING, + * registeredID [8] OBJECT IDENTIFIER + * } + * </pre> + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.7 + */ +public class GeneralName implements GeneralNameInterface { + + // Private data members + private GeneralNameInterface name = null; + + /** + * Default constructor for the class. + * + * @param name the selected CHOICE from the list. + */ + public GeneralName(GeneralNameInterface name) { + this.name = name; + } + + /** + * Create the object from its DER encoded value. + * + * @param encName the DER encoded GeneralName. + */ + public GeneralName(DerValue encName) throws IOException { + short tag = (byte)(encName.tag & 0x1f); + + // NB. this is always encoded with the IMPLICIT tag + // The checks only make sense if we assume implicit tagging, + // with explicit tagging the form is always constructed. + switch (tag) { + case GeneralNameInterface.NAME_RFC822: + if (encName.isContextSpecific() && !encName.isConstructed()) { + encName.resetTag(DerValue.tag_IA5String); + name = new RFC822Name(encName); + } else + throw new IOException("Invalid encoding of RFC822 name"); + break; + + case GeneralNameInterface.NAME_DNS: + if (encName.isContextSpecific() && !encName.isConstructed()) { + encName.resetTag(DerValue.tag_IA5String); + name = new DNSName(encName); + } else + throw new IOException("Invalid encoding of DNS name"); + break; + + case GeneralNameInterface.NAME_URI: + if (encName.isContextSpecific() && !encName.isConstructed()) { + encName.resetTag(DerValue.tag_IA5String); + name = new URIName(encName); + } else + throw new IOException("Invalid encoding of URI"); + break; + + case GeneralNameInterface.NAME_IP: + if (encName.isContextSpecific() && !encName.isConstructed()) { + encName.resetTag(DerValue.tag_OctetString); + name = new IPAddressName(encName); + } else + throw new IOException("Invalid encoding of IP address"); + break; + + case GeneralNameInterface.NAME_ANY: + if (encName.isContextSpecific() && encName.isConstructed()) { + encName.resetTag(DerValue.tag_OctetString); + name = new OtherName(encName); + } else + throw new IOException("Invalid encoding of other name"); + break; + + case GeneralNameInterface.NAME_OID: + if (encName.isContextSpecific() && !encName.isConstructed()) { + encName.resetTag(DerValue.tag_ObjectId); + name = new OIDName(encName); + } else + throw new IOException("Invalid encoding of OID name"); + break; + + case GeneralNameInterface.NAME_DIRECTORY: + if (encName.isContextSpecific() && encName.isConstructed()) { + // Unlike the other cases, DirectoryName is EXPLICITly + // tagged, because the X.500 Name type is a CHOICE. + // Therefore, the sequence is actually nested in the + // content of this value. We'll pretend it's an octet + // string so we can get at the content bytes. + encName.resetTag(DerValue.tag_OctetString); + byte[] content = encName.getOctetString(); + name = new X500Name( content ); + } else + throw new IOException("Invalid encoding of Directory name"); + break; + + case GeneralNameInterface.NAME_EDI: + if (encName.isContextSpecific() && encName.isConstructed()) { + encName.resetTag(DerValue.tag_Sequence); + name = new EDIPartyName(encName); + } else + throw new IOException("Invalid encoding of EDI name"); + break; + + default: + throw new IOException("Unrecognized GeneralName tag, (" + + tag +")"); + } + } + + /** + * Return the type of the general name. + */ + public int getType() { + return (name.getType()); + } + + /** + * Return the name as user readable string + */ + public String toString() { + return (name.toString()); + } + + /** + * Encode the name to the specified DerOutputStream. + * + * @param out the DerOutputStream to encode the the GeneralName to. + * @exception IOException on encoding errors. + */ + public void encode(DerOutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + name.encode(tmp); + int nameType = name.getType(); + boolean constructedForm; + + if (nameType == GeneralNameInterface.NAME_ANY || + nameType == GeneralNameInterface.NAME_X400 || + nameType == GeneralNameInterface.NAME_DIRECTORY || + nameType == GeneralNameInterface.NAME_EDI) { + constructedForm = true; + } else { + constructedForm = false; + } + + if( nameType == GeneralNameInterface.NAME_DIRECTORY ) { + // EXPLICIT tag, because Name is a CHOICE type + out.write(DerValue.createTag(DerValue.TAG_CONTEXT, + constructedForm, (byte)nameType), tmp); + } else { + // IMPLICIT tag, the default + out.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + constructedForm, (byte)nameType), tmp); + } + } +} diff --git a/pki/base/util/src/netscape/security/x509/GeneralNameInterface.java b/pki/base/util/src/netscape/security/x509/GeneralNameInterface.java new file mode 100644 index 00000000..1cf6ff6c --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/GeneralNameInterface.java @@ -0,0 +1,60 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; + +import netscape.security.util.*; + +/** + * This interface specifies the abstract methods which have to be + * implemented by all the members of the GeneralNames ASN.1 object. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.6 + */ +public interface GeneralNameInterface extends java.io.Serializable { + /** + * The list of names supported. + */ + public static final int NAME_ANY = 0; + public static final int NAME_RFC822 = 1; + public static final int NAME_DNS = 2; + public static final int NAME_X400 = 3; + public static final int NAME_DIRECTORY = 4; + public static final int NAME_EDI = 5; + public static final int NAME_URI = 6; + public static final int NAME_IP = 7; + public static final int NAME_OID = 8; + + /** + * Return the type of the general name, as + * defined above. + */ + int getType(); + + /** + * Encode the name to the specified DerOutputStream. + * + * @param out the DerOutputStream to encode the GeneralName to. + * @exception IOException thrown if the GeneralName could not be + * encoded. + */ + void encode(DerOutputStream out) throws IOException; +} diff --git a/pki/base/util/src/netscape/security/x509/GeneralNames.java b/pki/base/util/src/netscape/security/x509/GeneralNames.java new file mode 100644 index 00000000..d08eeb1a --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/GeneralNames.java @@ -0,0 +1,140 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.Vector; + +import netscape.security.util.*; + +/** + * This object class represents the GeneralNames type required in + * X509 certificates. + * <p>The ASN.1 syntax for this is: + * <pre> + * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + * </pre> + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.7 + */ +public class GeneralNames extends Vector { + /** + * Create the GeneralNames, decoding from the passed DerValue. + * + * <b>Caution when using this constructor. It may be broken! + * Better to call addElement(gni) directly where gni is + * a GeneralNameInterface object </b> + * + * @param derVal the DerValue to construct the GeneralNames from. + * @exception GeneralNamesException on decoding error. + * @exception IOException on error. + */ + public GeneralNames(DerValue derVal) + throws IOException, GeneralNamesException { + if (derVal.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for GeneralNames."); + } + if (derVal.data.available() == 0) { + throw new GeneralNamesException("No data available in " + + "passed DER encoded value."); + } + // Decode all the GeneralName's + while (derVal.data.available() != 0) { + DerValue encName = derVal.data.getDerValue(); + + GeneralName name = new GeneralName(encName); + addElement(name); + } + } + + /** + * Create the GeneralNames + * + * @param names a non-empty array of names to put into the + * generalNames + */ + + public GeneralNames(GeneralNameInterface[] names) + throws GeneralNamesException { + if (names == null || names.length==0) + throw new GeneralNamesException("Cannot create empty GeneralNames"); + + for (int i=0;i<names.length;i++) { + addElement(names[i]); + } + } + + + + /** + * The default constructor for this class. + */ + public GeneralNames() { + super(1,1); + } + + /** + * Write the extension to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to. + * @exception GeneralNamesException on encoding error. + * @exception IOException on error. + */ + public void encode(DerOutputStream out) + throws IOException, GeneralNamesException { + if (size() == 0) { return; } + + Enumeration names = elements(); + DerOutputStream temp = new DerOutputStream(); + + while (names.hasMoreElements()) { + Object obj = names.nextElement(); + if (!(obj instanceof GeneralNameInterface)) { + throw new GeneralNamesException("Element in GeneralNames " + + "not of type GeneralName."); + } + GeneralNameInterface intf = (GeneralNameInterface)obj; + if (obj instanceof GeneralName) { + intf.encode(temp); + } else { + DerOutputStream gname = new DerOutputStream(); + intf.encode(gname); + int nameType = intf.getType(); + // constructed form + if (nameType == GeneralNameInterface.NAME_ANY || + nameType == GeneralNameInterface.NAME_X400 || + nameType == GeneralNameInterface.NAME_EDI) { + + temp.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte)nameType), gname); + } else if ( nameType == GeneralNameInterface.NAME_DIRECTORY ) { + // EXPLICIT tag because directoryName is a CHOICE + temp.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte)nameType), gname); + } else // primitive form + temp.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + false, (byte)nameType), gname); + } + + } + + out.write(DerValue.tag_Sequence,temp); + } +} diff --git a/pki/base/util/src/netscape/security/x509/GeneralNamesException.java b/pki/base/util/src/netscape/security/x509/GeneralNamesException.java new file mode 100644 index 00000000..ddb0344b --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/GeneralNamesException.java @@ -0,0 +1,45 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.security.GeneralSecurityException; + +/** + * Generic General Names Exception. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.4 + */ +public class GeneralNamesException extends GeneralSecurityException { + /** + * Constructs a GeneralNamesException with no detail message. + */ + public GeneralNamesException() { + super(); + } + + /** + * Constructs the exception with the specified error message. + * + * @param message the requisite error message. + */ + public GeneralNamesException(String message) { + super(message); + } +} diff --git a/pki/base/util/src/netscape/security/x509/GeneralSubtree.java b/pki/base/util/src/netscape/security/x509/GeneralSubtree.java new file mode 100644 index 00000000..ff94ce4f --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/GeneralSubtree.java @@ -0,0 +1,158 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.*; + +import netscape.security.util.*; + +/** + * Represent the GeneralSubtree ASN.1 object, whose syntax is: + * <pre> + * GeneralSubtree ::= SEQUENCE { + * base GeneralName, + * minimum [0] BaseDistance DEFAULT 0, + * maximum [1] BaseDistance OPTIONAL + * } + * BaseDistance ::= INTEGER (0..MAX) + * </pre> + * @version 1.5 + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class GeneralSubtree { + private static final byte TAG_MIN = 0; + private static final byte TAG_MAX = 1; + private static final int MIN_DEFAULT = 0; + + private GeneralName name; + private int minimum = MIN_DEFAULT; + private int maximum = -1; + + private PrettyPrintFormat pp = new PrettyPrintFormat(":"); + + /** + * The default constructor for the class. + * + * @param name the GeneralName + * @param min the minimum BaseDistance + * @param max the maximum BaseDistance + */ + public GeneralSubtree(GeneralName name, int min, int max) { + this.name = name; + this.minimum = min; + this.maximum = max; + } + + /** + * Create the object from its DER encoded form. + * + * @param val the DER encoded from of the same. + */ + public GeneralSubtree(DerValue val) throws IOException { + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for GeneralSubtree."); + } + name = new GeneralName(val.data.getDerValue()); + + // NB. this is always encoded with the IMPLICIT tag + // The checks only make sense if we assume implicit tagging, + // with explicit tagging the form is always constructed. + while (val.data.available() != 0) { + DerValue opt = val.data.getDerValue(); + + if (opt.isContextSpecific(TAG_MIN) && !opt.isConstructed()) { + opt.resetTag(DerValue.tag_Integer); + minimum = (opt.getInteger()).toInt(); + + } else if (opt.isContextSpecific(TAG_MAX) && !opt.isConstructed()) { + opt.resetTag(DerValue.tag_Integer); + maximum = (opt.getInteger()).toInt(); + } else + throw new IOException("Invalid encoding of GeneralSubtree."); + } + } + + /** + * Return a printable string of the GeneralSubtree. + */ + public String toString() { + String s = "\n GeneralSubtree: [\n" + + " GeneralName: " + ((name == null) ? "" : name.toString()) + + "\n Minimum: " + minimum; + if (maximum == -1) { + s += "\t Maximum: undefined"; + } else + s += "\t Maximum: " + maximum; + s += " ]\n"; + return (s); + } + + public String toPrint(int indent) { + String s = "\n"+pp.indent(indent) + "GeneralSubtree: [\n" + pp.indent(indent+2) + + "GeneralName: " + ((name == null) ? "" : name.toString()) + + "\n"+pp.indent(indent+2) + "Minimum: " + minimum; + if (maximum == -1) { + s += "\n" + pp.indent(indent+2) + "Maximum: undefined"; + } else + s += "\n" + pp.indent(indent+2) + "Maximum: " + maximum; + s += "]\n"; + return (s); + } + + /** + * Encode the GeneralSubtree. + * + * @param out the DerOutputStream to encode this object to. + */ + public void encode(DerOutputStream out) throws IOException { + DerOutputStream seq = new DerOutputStream(); + + name.encode(seq); + + // XXX NSS 2.8 crashes when minimum is not set. + // always encode minimum as a workaround. + // REMOVE WHEN NSS HAS FIXED THE PROBLEM. + // if (minimum != MIN_DEFAULT) + { + DerOutputStream tmp = new DerOutputStream(); + tmp.putInteger(new BigInt(minimum)); + seq.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + false, TAG_MIN), tmp); + } + if (maximum != -1) { + DerOutputStream tmp = new DerOutputStream(); + tmp.putInteger(new BigInt(maximum)); + seq.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + false, TAG_MAX), tmp); + } + out.write(DerValue.tag_Sequence,seq); + } + + public GeneralName getGeneralName() { + return name; + } + + public int getMaxValue() { + return maximum; + } + + public int getMinValue() { + return minimum; + } +} diff --git a/pki/base/util/src/netscape/security/x509/GeneralSubtrees.java b/pki/base/util/src/netscape/security/x509/GeneralSubtrees.java new file mode 100644 index 00000000..96537e88 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/GeneralSubtrees.java @@ -0,0 +1,103 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.*; +import java.util.*; + +import netscape.security.util.*; + +/** + * Represent the GeneralSubtrees ASN.1 object. + * + * @version 1.4 + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class GeneralSubtrees { + private Vector trees; + private PrettyPrintFormat pp = new PrettyPrintFormat(":"); + + /** + * The default constructor for the class. + * + * @param trees the sequence of GeneralSubtree. + */ + public GeneralSubtrees(Vector trees) { + this.trees = trees; + } + + /** + * Create the object from the passed DER encoded form. + * + * @param val the DER encoded form of the same. + */ + public GeneralSubtrees(DerValue val) throws IOException { + trees = new Vector(1, 1); + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding of GeneralSubtrees."); + } + while (val.data.available() != 0) { + DerValue opt = val.data.getDerValue(); + GeneralSubtree tree = new GeneralSubtree(opt); + trees.addElement(tree); + } + } + + /** + * Return a printable string of the GeneralSubtree. + */ + public String toString() { + String s = " GeneralSubtrees:\n" + trees.toString() + + "\n"; + + return (s); + } + + public String toPrint(int indent) { + + String s = ""; + GeneralSubtree element; + + for (Enumeration e = trees.elements() ; e.hasMoreElements() ;) { + element = (GeneralSubtree) e.nextElement(); + s = s + pp.indent(indent+4)+ element.toPrint(indent) +"\n"; + } + + return (s); + } + + /** + * Encode the GeneralSubtrees. + * + * @param out the DerOutputStrean to encode this object to. + */ + public void encode(DerOutputStream out) throws IOException { + DerOutputStream seq = new DerOutputStream(); + + for (int i = 0; i < trees.size(); i++) { + ((GeneralSubtree)trees.elementAt(i)).encode(seq); + } + out.write(DerValue.tag_Sequence,seq); + } + + public Vector getSubtrees() { + return trees; + } +} diff --git a/pki/base/util/src/netscape/security/x509/GenericValueConverter.java b/pki/base/util/src/netscape/security/x509/GenericValueConverter.java new file mode 100644 index 00000000..ba80bfc2 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/GenericValueConverter.java @@ -0,0 +1,169 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.*; +import sun.io.CharToByteConverter; +import sun.io.ByteToCharConverter; + +import netscape.security.util.DerValue; +import netscape.security.util.ASN1CharStrConvMap; + +/** + * A GenericValueConverter converts a string that is not associated with + * a particular attribute to a DER encoded ASN.1 character string type. + * Currently supports PrintableString, IA5String, BMPString T.61String and + * Universal String. + * + * <p>The conversion is done as follows. + * A CharToByteConverter is obtained for the all the character sets + * from the global default ASN1CharStrConvMap. + * The CharToByteConverters are then used to convert the string to the + * smallest character set first -- printableString. + * If the string contains characters outside of that character set, + * it is converted to the next character set -- IA5String character set. + * If that is not enough it is converted to a BMPString, then + * Universal String which contains all characters. + * + * @author Lily Hsiao, Slava Galperin at Netscape Communications, Inc. + * + */ + +public class GenericValueConverter implements AVAValueConverter +{ + public GenericValueConverter() + { + } + + /** + * Converts a string to a DER encoded ASN.1 primtable string, defined here + * as a PrintableString, IA5String, T.61String, BMPString or + * UniversalString. The string is not expected to be encoded in any form. + * + * <p>If a CharToByteConverter is not available for a character set that + * is needed to convert the string, the string cannot be converted and + * an IOException is thrown. For example, if the string contains characters + * outside the PrintableString character and only a PrintableString + * CharToByteConverter is available then an IOException is thrown. + * + * @param s A string representing a generic attribute string value. + * + * @return The DER value of the attribute. + * + * @exception IOException if the string cannot be converted, such as + * when a CharToByteConverter needed is + * unavailable. + */ + public DerValue getValue(String s) + throws IOException + { + return getValue(s, null); + } + + public DerValue getValue(String s, byte[] tags) + throws IOException + { + // try to convert to printable, then t61 the universal - + // i.e. from minimal coverage to the broadest. + int ret = -1; + CharToByteConverter cbc; + DerValue value; + byte[] bbuf, derBuf; + int i; + + if (tags == null || tags.length == 0) + tags = DefEncodingTags; + + bbuf = new byte[4*s.length()]; + for (i = 0; i < tags.length; i++) + { + try { + cbc = ASN1CharStrConvMap.getDefault().getCBC(tags[i]); + if (cbc == null) + continue; + ret = cbc.convert(s.toCharArray(), 0, s.length(), + bbuf, 0, bbuf.length); + break; + } + catch (java.io.CharConversionException e) { + continue; + } + catch (InstantiationException e) { + throw new IOException("Cannot instantiate CharToByteConverter"); + } + catch (IllegalAccessException e) { + throw new IOException( + "Illegal Access loading CharToByteConverter"); + } + } + if (ret == -1) { + throw new IllegalArgumentException( + "Cannot convert the string value to a ASN.1 type"); + } + + derBuf = new byte[ret]; + System.arraycopy(bbuf, 0, derBuf, 0, ret); + return new DerValue(tags[i], derBuf); + } + + /** + * Creates a DerValue from the byte array of BER encoded value. + * + * NOTE: currently only supports DER encoding (a form of BER) on input . + * + * @param berStream Byte array of a BER encoded value. + * + * @return DerValue object. + * + * @exception IOException If the BER value cannot be converted to a + * valid Directory String DER value. + */ + public DerValue getValue(byte[] berByteStream) + throws IOException + { + // accepts any tag. + DerValue value = new DerValue(berByteStream); + return value; + } + + /** + * Converts a DerValue of ASN1 Character string type to a java string + * (the string is not encoded in any form). + * + * @param avaValue A DerValue + * @return A string representing the attribute value. + * @exception IOException if a ByteToCharConverter needed for the + * conversion is not available or if BER value + * is not one of the ASN1 character string types + * here. + */ + public String getAsString(DerValue avaValue) + throws IOException + { + return avaValue.getASN1CharString(); + } + + private static byte DefEncodingTags[] = { + DerValue.tag_PrintableString, + DerValue.tag_IA5String, + DerValue.tag_BMPString, + DerValue.tag_UTF8String, + DerValue.tag_T61String, + DerValue.tag_UniversalString + }; +} diff --git a/pki/base/util/src/netscape/security/x509/HoldInstructionExtension.java b/pki/base/util/src/netscape/security/x509/HoldInstructionExtension.java new file mode 100644 index 00000000..b36f3018 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/HoldInstructionExtension.java @@ -0,0 +1,361 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.math.BigInteger; +import java.util.Enumeration; +import java.util.Date; +import java.security.cert.CertificateException; + +import netscape.security.util.*; + + +/** + * Represent the CRL Hold Instruction Code Extension. + * + * <p>The hold instruction code is a non-critical CRL entry + * extension that provides a registered instruction identifier + * which indicates the action to be taken after encountering + * a certificate that has been placed on hold. + * + * @see Extension + * @see CertAttrSet + */ + + +public class HoldInstructionExtension extends Extension +implements CertAttrSet { + + /** + * Attribute name. + */ + public static final String NAME = "HoldInstruction"; + public static final String HOLD_INSTRUCTION = "value"; + + /** + * The Object Identifier for this extension. + */ + public static final String OID = "2.5.29.23"; + + public static final String NONE_HOLD_INSTR_OID_STR = + "1.2.840.10040.2.1"; + public static final ObjectIdentifier NONE_HOLD_INSTR_OID = + new ObjectIdentifier(NONE_HOLD_INSTR_OID_STR); + + public static final String CALL_ISSUER_HOLD_INSTR_OID_STR = + "1.2.840.10040.2.2"; + public static final ObjectIdentifier CALL_ISSUER_HOLD_INSTR_OID = + new ObjectIdentifier(CALL_ISSUER_HOLD_INSTR_OID_STR); + + public static final String REJECT_HOLD_INSTR_OID_STR = + "1.2.840.10040.2.3"; + public static final ObjectIdentifier REJECT_HOLD_INSTR_OID = + new ObjectIdentifier(REJECT_HOLD_INSTR_OID_STR); + + private ObjectIdentifier holdInstructionCodeOIDs[] = {NONE_HOLD_INSTR_OID, + CALL_ISSUER_HOLD_INSTR_OID, + REJECT_HOLD_INSTR_OID}; + private ObjectIdentifier holdInstructionCodeOID = null; + + private String holdInstructionDescription[] = {"None", + "Call Issuer", + "Reject"}; + + + static { + try { + OIDMap.addAttribute(HoldInstructionExtension.class.getName(), + OID, NAME); + } catch (CertificateException e) {} + } + + private int getHoldInstructionCodeFromOID(ObjectIdentifier oid) { + for (int i = 0; i < holdInstructionCodeOIDs.length; i++) { + if (oid.equals(holdInstructionCodeOIDs[i])) + return (i + 1); + } + return 0; + } + + private String getHoldInstructionDescription(ObjectIdentifier oid) { + String description = "Invalid"; + if (oid != null) { + int i = getHoldInstructionCodeFromOID(oid); + if (i > 0 && i < 4) + description = holdInstructionDescription[i-1]; + } + return (description); + } + + // Encode this extension value + private void encodeThis() throws IOException { + if (holdInstructionCodeOID == null) + throw new IOException("Unintialized hold instruction extension"); + DerOutputStream os = new DerOutputStream(); + os.putOID(holdInstructionCodeOID); + this.extensionValue = os.toByteArray(); + } + + /** + * Create a HoldInstructionExtension with the date. + * The criticality is set to false. + * + * @param code the value to be set for the extension. + */ + public HoldInstructionExtension(int code) + throws IOException + { + if (code < 1 || code > 3) + throw new IOException("Invalid hold instruction code"); + holdInstructionCodeOID = holdInstructionCodeOIDs[code-1]; + this.extensionId = PKIXExtensions.HoldInstructionCode_Id; + this.critical = false; + encodeThis(); + } + + /** + * Create a HoldInstructionExtension with the date. + * The criticality is set to false. + * + * @param oidStr the value to be set for the extension. + */ + public HoldInstructionExtension(String oidStr) + throws IOException + { + ObjectIdentifier oid = new ObjectIdentifier(oidStr); + if (oid == null || getHoldInstructionCodeFromOID(oid) == 0) + throw new IOException("Invalid hold instruction code"); + holdInstructionCodeOID = oid; + this.extensionId = PKIXExtensions.HoldInstructionCode_Id; + this.critical = false; + encodeThis(); + } + + /** + * Create a HoldInstructionExtension with the date. + * The criticality is set to false. + * + * @param oid the value to be set for the extension. + */ + public HoldInstructionExtension(ObjectIdentifier oid) + throws IOException + { + if (getHoldInstructionCodeFromOID(oid) == 0) + throw new IOException("Invalid hold instruction code"); + holdInstructionCodeOID = oid; + this.extensionId = PKIXExtensions.HoldInstructionCode_Id; + this.critical = false; + encodeThis(); + } + + /** + * Create a HoldInstructionExtension with the date. + * The criticality is set to false. + * + * @param critical true if the extension is to be treated as critical. + * @param code the value to be set for the extension. + */ + public HoldInstructionExtension(Boolean critical, int code) + throws IOException + { + if (code < 1 || code > 3) + throw new IOException("Invalid hold instruction code"); + holdInstructionCodeOID = holdInstructionCodeOIDs[code-1]; + this.extensionId = PKIXExtensions.HoldInstructionCode_Id; + this.critical = critical.booleanValue(); + encodeThis(); + } + + /** + * Create a HoldInstructionExtension with the date. + * The criticality is set to false. + * + * @param critical true if the extension is to be treated as critical. + * @param oidStr the value to be set for the extension. + */ + public HoldInstructionExtension(Boolean critical, String oidStr) + throws IOException + { + ObjectIdentifier oid = new ObjectIdentifier(oidStr); + if (oid == null || getHoldInstructionCodeFromOID(oid) == 0) + throw new IOException("Invalid hold instruction code"); + holdInstructionCodeOID = oid; + this.extensionId = PKIXExtensions.HoldInstructionCode_Id; + this.critical = critical.booleanValue(); + encodeThis(); + } + + /** + * Create a HoldInstructionExtension with the date. + * The criticality is set to false. + * + * @param critical true if the extension is to be treated as critical. + * @param oid the value to be set for the extension. + */ + public HoldInstructionExtension(Boolean critical, ObjectIdentifier oid) + throws IOException + { + if (getHoldInstructionCodeFromOID(oid) == 0) + throw new IOException("Invalid hold instruction code"); + holdInstructionCodeOID = oid; + this.extensionId = PKIXExtensions.HoldInstructionCode_Id; + this.critical = critical.booleanValue(); + encodeThis(); + } + + /** + * Create the extension from the passed DER encoded value of the same. + * + * @param critical true if the extension is to be treated as critical. + * @param value Array of DER encoded bytes of the actual value. + * @exception IOException on error. + */ + public HoldInstructionExtension(Boolean critical, Object value) + throws IOException + { + this.extensionId = PKIXExtensions.HoldInstructionCode_Id; + this.critical = critical.booleanValue(); + + int len = Array.getLength(value); + byte[] extValue = new byte[len]; + for (int i = 0; i < len; i++) { + extValue[i] = Array.getByte(value, i); + } + this.extensionValue = extValue; + DerValue val = new DerValue(extValue); + if (val.tag == DerValue.tag_ObjectId) { + DerInputStream derInputStream = new DerInputStream(val.toByteArray()); + holdInstructionCodeOID = derInputStream.getOID(); + if (getHoldInstructionCodeFromOID(holdInstructionCodeOID) == 0) + throw new IOException("Invalid encoding for HoldInstructionExtension"); + } else { + throw new IOException("Invalid encoding for HoldInstructionExtension"); + } + } + + /** + * Get the hold instruction code. + */ + public ObjectIdentifier getHoldInstructionCode() + { + return holdInstructionCodeOID; + } + + public String getHoldInstructionCodeDescription() + { + return getHoldInstructionDescription(holdInstructionCodeOID); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (name.equalsIgnoreCase(HOLD_INSTRUCTION)) { + if (!(obj instanceof ObjectIdentifier)) { + throw new IOException("Attribute must be of type String."); + } + holdInstructionCodeOID = (ObjectIdentifier)obj; + } else { + throw new IOException("Attribute name not recognized by"+ + " CertAttrSet:HoldInstructionCode."); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(HOLD_INSTRUCTION)) { + return holdInstructionCodeOID; + } else { + throw new IOException("Attribute name not recognized by"+ + " CertAttrSet:HoldInstructionCode."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(HOLD_INSTRUCTION)) { + holdInstructionCodeOID = null; + } else { + throw new IOException("Attribute name not recognized by"+ + " CertAttrSet:HoldInstructionCode."); + } + } + + /** + * Returns a printable representation of the HoldInstructionExtension. + */ + public String toString() { + String s = super.toString() + "Hold Instruction Code: "+ + getHoldInstructionDescription(holdInstructionCodeOID)+"\n"; + return (s); + } + + /** + * Decode the extension from the InputStream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception IOException on decoding or validity errors. + */ + public void decode(InputStream in) throws IOException { + throw new IOException("Method not to be called directly."); + } + + /** + * Write the extension to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + + if (this.extensionValue == null) { + this.extensionId = PKIXExtensions.HoldInstructionCode_Id; + this.critical = true; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements () { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(HOLD_INSTRUCTION); + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName () { + return (NAME); + } +} + diff --git a/pki/base/util/src/netscape/security/x509/IA5StringConverter.java b/pki/base/util/src/netscape/security/x509/IA5StringConverter.java new file mode 100644 index 00000000..80572a68 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/IA5StringConverter.java @@ -0,0 +1,131 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.*; +import sun.io.CharToByteConverter; + +import netscape.security.util.*; + +/** + * A AVAValueConverter that converts a IA5String attribute to a DerValue + * and vice versa. An example an attribute that is a IA5String string is "E". + * @see AVAValueConverter + * + * @author Lily Hsiao, Slava Galperin at Netscape Communications, Inc. + */ + +public class IA5StringConverter implements AVAValueConverter +{ + // public constructors + + /* + * Contructs a IA5String Converter. + */ + public IA5StringConverter() + { + } + + /* + * Converts a string with ASN.1 IA5String characters to a DerValue. + * + * @param valueString a string with IA5String characters. + * + * @return a DerValue. + * + * @exception IOException if a IA5String CharToByteConverter is not + * available for the conversion. + */ + public DerValue getValue(String valueString) + throws IOException + { + return getValue(valueString, null); + } + + public DerValue getValue(String valueString, byte[] encodingOrder) + throws IOException + { + ASN1CharStrConvMap map; + CharToByteConverter cbc; + byte[] bbuf = new byte[valueString.length()]; + map = ASN1CharStrConvMap.getDefault(); + try { + cbc = map.getCBC(DerValue.tag_IA5String); + if (cbc == null) + throw new IOException("No CharToByteConverter for IA5String"); + cbc.convert(valueString.toCharArray(), 0, valueString.length(), + bbuf, 0, bbuf.length); + } + catch (java.io.CharConversionException e) { + throw new IllegalArgumentException( + "Invalid IA5String AVA Value string"); + } catch (InstantiationException e) { + throw new IOException("Cannot instantiate CharToByteConverter"); + } catch (IllegalAccessException e) { + throw new IOException("Illegal access loading CharToByteConverter"); + } + return new DerValue(DerValue.tag_IA5String, bbuf); + } + + /* + * Converts a BER encoded value of IA5String to a DER encoded value. + * Checks if the BER encoded value is a IA5String. + * NOTE only DER encoding is currently supported on for the BER + * encoded value. + * + * @param berStream a byte array of the BER encoded value. + * + * @return a DerValue. + * + * @exception IOException if the BER value cannot be converted + * to a IA5String DER value. + */ + public DerValue getValue(byte[] berStream) + throws IOException + { + DerValue value = new DerValue(berStream); + if (value.tag == DerValue.tag_IA5String) + return value; + if (value.tag == DerValue.tag_PrintableString) + return value; + throw new IOException("Invalid IA5String AVA Value."); + } + + /* + * Converts a DerValue of IA5String to a java string with IA5String + * characters. + * + * @param avaValue a DerValue. + * + * @return a string with IA5String characters. + * + * @exception IOException if the DerValue is not a IA5String i.e. + * The DerValue cannot be converted to a string + * with IA5String characters. + */ + public String getAsString(DerValue avaValue) + throws IOException + { + if (avaValue.tag == DerValue.tag_IA5String) + return avaValue.getIA5String(); + if (avaValue.tag == DerValue.tag_PrintableString) + return avaValue.getPrintableString(); + throw new IOException("Invalid IA5String AVA Value."); + } + +} diff --git a/pki/base/util/src/netscape/security/x509/IPAddressName.java b/pki/base/util/src/netscape/security/x509/IPAddressName.java new file mode 100644 index 00000000..caa5bcaf --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/IPAddressName.java @@ -0,0 +1,253 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.*; +import java.util.StringTokenizer; +import java.lang.Integer; + +import netscape.security.util.*; + +/** + * This class implements the IPAddressName as required by the GeneralNames + * ASN.1 object. + * + * @see GeneralName + * @see GeneralNameInterface + * @see GeneralNames + * + * @version 1.2 + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class IPAddressName implements GeneralNameInterface { + private byte[] address; + + /** + * Create the IPAddressName object from the passed encoded Der value. + * + * @param derValue the encoded DER IPAddressName. + * @exception IOException on error. + */ + public IPAddressName(DerValue derValue) throws IOException { + address = derValue.getOctetString(); + } + + /** + * Create the IPAddressName object with the specified name. + * + * @param name the IPAddressName. + */ + public IPAddressName(byte[] address) { + this.address = address; + } + + protected static final char IPv4_LEN = 4; + protected static final char IPv6_LEN = 16; + protected static final IPAddr IPv4 = new IPv4Addr(); + protected static final IPAddr IPv6 = new IPv6Addr(); + + /** + * Create the IPAddressName object with a string representing the + * ip address and a string representing the netmask, with encoding + * having ip address encoding followed by the netmask encoding. + * This form is needed for name constraints extension. + * + * @param s the ip address in the format: n.n.n.n or x:x:x:x:x:x:x:x (RFC 1884) + * @param netmask the netmask address in the format: n.n.n.n or x:x:x:x:x:x:x:x (RFC 1884) + */ + public IPAddressName(String s, String netmask) + { + // Based on PKIX RFC2459. IPAddress has + // 8 bytes (instead of 4 bytes) in the + // context of NameConstraints + IPAddr ipAddr = null; + if (s.indexOf(':') != -1) { + ipAddr = IPv6; + address = new byte[IPv6_LEN*2]; + } else { + ipAddr = IPv4; + address = new byte[IPv4_LEN*2]; + } + StringTokenizer st = new StringTokenizer(s,","); + int numFilled = ipAddr.getIPAddr(st.nextToken(), address, 0); + if (st.hasMoreTokens()) { + ipAddr.getIPAddr(st.nextToken(), address, numFilled); + } + else { + for (int i=numFilled; i < address.length; i++) + address[i] = (byte)0xff; + } + } + + + /** + * Create the IPAddressName object with a string representing the + * ip address. + * + * @param s the ip address in the format: n.n.n.n or x:x:x:x:x:x:x:x + */ + public IPAddressName(String s) { + IPAddr ipAddr = null; + if (s.indexOf(':') != -1) { + ipAddr = IPv6; + address = new byte[IPv6_LEN*2]; + } else { + ipAddr = IPv4; + address = new byte[IPv4_LEN*2]; + } + ipAddr.getIPAddr(s, address, 0); + } + + /** + * Return the type of the GeneralName. + */ + public int getType() { + return (GeneralNameInterface.NAME_IP); + } + + /** + * Encode the IPAddress name into the DerOutputStream. + * + * @param out the DER stream to encode the IPAddressName to. + * @exception IOException on encoding errors. + */ + public void encode(DerOutputStream out) throws IOException { + out.putOctetString(address); + } + + /** + * Return a printable string of IPaddress + */ + public String toString() { + return ("IPAddress: " + (address[0] & 0xff) + "." + + (address[1] & 0xff) + "." + + (address[2] & 0xff) + "." + + (address[3] & 0xff)); + } +} + +interface IPAddr +{ + public int getIPAddr(String s, byte[] address, int start); + public int getLength(); +} + +class IPv4Addr implements IPAddr +{ + protected static final int IPv4_LEN = 4; + + /** + * Gets an IP v4 address in the form n.n.n.n. + */ + public int getIPAddr(String s, byte[] address, int start) { + StringTokenizer st = new StringTokenizer(s,"."); + int nt = st.countTokens(); + if (nt != IPv4_LEN) + throw new InvalidIPAddressException(s); + try { + int end = start+nt; + for (int i=start; i<end; i++) { + Integer j = new Integer(st.nextToken()); + address[i] = (byte)j.intValue(); + } + } catch (NumberFormatException e) { + throw new InvalidIPAddressException(s); + } + return nt; + } + + public int getLength() { return IPv4_LEN; } +} + +class IPv6Addr implements IPAddr +{ + /** + * Gets an IP address in the forms as defined in RFC1884:<br> + * <ul> + * <li>x:x:x:x:x:x:x:x + * <li>...::xxx (using :: shorthand) + * <li>...:n.n.n.n (with n.n.n.n at the end) + * </ul> + */ + public int getIPAddr(String s, byte[] address, int start) { + int lastcolon = -2; + int end = start+16; + int idx = start; + for (int i = start; i < address.length; i++) + address[i] = 0; + if (s.indexOf('.') != -1) { // has n.n.n.n at the end + lastcolon = s.lastIndexOf(':'); + if (lastcolon == -1) + throw new InvalidIPAddressException(s); + end -= 4; + IPAddressName.IPv4.getIPAddr( + s.substring(lastcolon+1), address, end); + } + try { + String s1 = s; + if (lastcolon != -2) + s1 = s.substring(0, lastcolon+1); + int lastDoubleColon = s1.indexOf("::"); + String l = s1, r = null; + StringTokenizer lt = null, rt = null; + if (lastDoubleColon != -1) { + l = s1.substring(0, lastDoubleColon); + r = s1.substring(lastDoubleColon+2); + if (l.length() == 0) l = null; + if (r.length() == 0) r = null; + } + int at = 0; + if (l != null) { + lt = new StringTokenizer(l,":", false); + at += lt.countTokens(); + } + if (r != null) { + rt = new StringTokenizer(r,":", false); + at += rt.countTokens(); + } + if (at > 8 || + (lastcolon!=-2 && (at>6 || (lastDoubleColon==-1 && at!=6)))) + throw new InvalidIPAddressException(s); + if (l != null) { + while(lt.hasMoreTokens()) { + String tok = lt.nextToken(); + int j = Integer.parseInt(tok, 16); + address[idx++] = (byte)((j>>8) & 0xFF); + address[idx++] = (byte)(j & 0xFF); + } + } + if (r != null) { + idx = end-(rt.countTokens()*2); + while(rt.hasMoreTokens()) { + String tok = rt.nextToken(); + int j = Integer.parseInt(tok, 16); + address[idx++] = (byte)((j>>8) & 0xFF); + address[idx++] = (byte)(j & 0xFF); + } + } + } catch (NumberFormatException e) { + throw new InvalidIPAddressException(s); + } + return 16; + } + + public int getLength() { return 16; } +} + diff --git a/pki/base/util/src/netscape/security/x509/InvalidIPAddressException.java b/pki/base/util/src/netscape/security/x509/InvalidIPAddressException.java new file mode 100644 index 00000000..783a4bdd --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/InvalidIPAddressException.java @@ -0,0 +1,28 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +public class InvalidIPAddressException extends RuntimeException +{ + public InvalidIPAddressException() { + super(); + } + public InvalidIPAddressException(String ip) { + super("Invalid IP Address '"+ip+"'"); + } +} diff --git a/pki/base/util/src/netscape/security/x509/InvalidityDateExtension.java b/pki/base/util/src/netscape/security/x509/InvalidityDateExtension.java new file mode 100755 index 00000000..47a5d910 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/InvalidityDateExtension.java @@ -0,0 +1,240 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.math.BigInteger; +import java.util.Enumeration; +import java.util.Date; +import java.security.cert.CertificateException; + +import netscape.security.util.*; + + +/** + * Represent the CRL Invalidity Date Extension. + * + * <p>This CRL entry extension, if present, provides the date + * on which it is known or suspected that the private key was + * compromised or that the certificate otherwise became invalid. + * Invalidity date may be earlier than the revocation date. + * + * @see Extension + * @see CertAttrSet + */ + + +public class InvalidityDateExtension extends Extension +implements CertAttrSet { + + /** + * Attribute name. + */ + public static final String NAME = "InvalidityDate"; + public static final String INVALIDITY_DATE = "value"; + + /** + * The Object Identifier for this extension. + */ + public static final String OID = "2.5.29.24"; + + + private Date invalidityDate = null; + + static { + try { + OIDMap.addAttribute(InvalidityDateExtension.class.getName(), + OID, NAME); + } catch (CertificateException e) {} + } + + // Encode this extension value + private void encodeThis() throws IOException { + if (invalidityDate == null) + throw new IOException("Unintialized invalidity date extension"); + DerOutputStream os = new DerOutputStream(); + os.putGeneralizedTime(this.invalidityDate); + this.extensionValue = os.toByteArray(); + } + + /** + * Create a InvalidityDateExtension with the date. + * The criticality is set to false. + * + * @param dateOfInvalidity the value to be set for the extension. + */ + public InvalidityDateExtension(Date dateOfInvalidity) + throws IOException + { + this.invalidityDate = dateOfInvalidity; + this.extensionId = PKIXExtensions.InvalidityDate_Id; + this.critical = false; + encodeThis(); + } + + /** + * Create a InvalidityDateExtension with the date. + * The criticality is set to false. + * + * @param critical true if the extension is to be treated as critical. + * @param dateOfInvalidity the value to be set for the extension. + */ + public InvalidityDateExtension(Boolean critical, Date dateOfInvalidity) + throws IOException + { + this.invalidityDate = dateOfInvalidity; + this.extensionId = PKIXExtensions.InvalidityDate_Id; + this.critical = critical.booleanValue(); + encodeThis(); + } + + /** + * Create the extension from the passed DER encoded value of the same. + * + * @param critical true if the extension is to be treated as critical. + * @param value Array of DER encoded bytes of the actual value. + * @exception IOException on error. + */ + public InvalidityDateExtension(Boolean critical, Object value) + throws IOException + { + this.extensionId = PKIXExtensions.InvalidityDate_Id; + this.critical = critical.booleanValue(); + + int len = Array.getLength(value); + byte[] extValue = new byte[len]; + for (int i = 0; i < len; i++) { + extValue[i] = Array.getByte(value, i); + } + this.extensionValue = extValue; + DerValue val = new DerValue(extValue); + if (val.tag == DerValue.tag_GeneralizedTime) { + DerInputStream derInputStream = new DerInputStream(val.toByteArray()); + this.invalidityDate = derInputStream.getGeneralizedTime(); + } else { + throw new IOException("Invalid encoding for InvalidityDateExtension"); + } + } + + /** + * Get the invalidity date. + */ + public Date getInvalidityDate() + { + return invalidityDate; + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (name.equalsIgnoreCase(INVALIDITY_DATE)) { + if (!(obj instanceof Date)) { + throw new IOException("Attribute must be of type Date."); + } + invalidityDate = (Date)obj; + } else { + throw new IOException("Attribute name not recognized by"+ + " CertAttrSet:InvalidityDate."); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(INVALIDITY_DATE)) { + if (invalidityDate == null) return null; + else return invalidityDate; + } else { + throw new IOException("Attribute name not recognized by"+ + " CertAttrSet:InvalidityDate."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(INVALIDITY_DATE)) { + invalidityDate = null; + } else { + throw new IOException("Attribute name not recognized by"+ + " CertAttrSet:InvalidityDate."); + } + } + + /** + * Returns a printable representation of the InvalidityDateExtension. + */ + public String toString() { + String s = super.toString() + "Invalidity Date: " + + ((invalidityDate == null) ? "" : invalidityDate.toString()) + + "\n"; + return (s); + } + + /** + * Decode the extension from the InputStream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception IOException on decoding or validity errors. + */ + public void decode(InputStream in) throws IOException { + throw new IOException("Method not to be called directly."); + } + + /** + * Write the extension to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + + if (this.extensionValue == null) { + this.extensionId = PKIXExtensions.InvalidityDate_Id; + this.critical = true; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements () { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(INVALIDITY_DATE); + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName () { + return (NAME); + } +} + diff --git a/pki/base/util/src/netscape/security/x509/IssuerAlternativeNameExtension.java b/pki/base/util/src/netscape/security/x509/IssuerAlternativeNameExtension.java new file mode 100644 index 00000000..d0dc7a5c --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/IssuerAlternativeNameExtension.java @@ -0,0 +1,233 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.util.Enumeration; + +import netscape.security.util.*; + +/** + * This represents the Issuer Alternative Name Extension. + * + * This extension, if present, allows the issuer to specify multiple + * alternative names. + * + * <p>Extensions are represented as a sequence of the extension identifier + * (Object Identifier), a boolean flag stating whether the extension is to + * be treated as being critical and the extension value itself (this is again + * a DER encoding of the extension value). + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.7 + * @see Extension + * @see CertAttrSet + */ +public class IssuerAlternativeNameExtension +extends Extension implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = + "x509.info.extensions.IssuerAlternativeName"; + /** + * Attribute names. + */ + public static final String NAME = "IssuerAlternativeName"; + public static final String ISSUER_NAME = "issuer_name"; + + // private data members + GeneralNames names; + + // Encode this extension + private void encodeThis() throws IOException { + DerOutputStream os = new DerOutputStream(); + try { + names.encode(os); + } catch (GeneralNamesException e) { + throw new IOException(e.toString()); + } + this.extensionValue = os.toByteArray(); + } + + /** + * Create a IssuerAlternativeNameExtension with the passed GeneralNames. + * + * @param critical true if the extension is to be treated as critical. + * @param names the GeneralNames for the issuer. + * @exception IOException on error. + */ + public IssuerAlternativeNameExtension(Boolean critical, GeneralNames names) + throws IOException { + this.names = names; + this.extensionId = PKIXExtensions.IssuerAlternativeName_Id; + this.critical = critical.booleanValue(); + encodeThis(); + } + + /** + * Create a IssuerAlternativeNameExtension with the passed GeneralNames. + * + * @param names the GeneralNames for the issuer. + * @exception IOException on error. + */ + public IssuerAlternativeNameExtension(GeneralNames names) + throws IOException { + this.names = names; + this.extensionId = PKIXExtensions.IssuerAlternativeName_Id; + this.critical = false; + encodeThis(); + } + + /** + * Create a default IssuerAlternativeNameExtension. + */ + public IssuerAlternativeNameExtension() { + extensionId = PKIXExtensions.IssuerAlternativeName_Id; + critical = false; + names = new GeneralNames(); + } + + /** + * Create the extension from the passed DER encoded value. + * + * @param critical true if the extension is to be treated as critical. + * @param value Array of DER encoded bytes of the actual value. + * @exception IOException on error. + */ + public IssuerAlternativeNameExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.IssuerAlternativeName_Id; + this.critical = critical.booleanValue(); + + int len = Array.getLength(value); + byte[] extValue = new byte[len]; + for (int i = 0; i < len; i++) { + extValue[i] = Array.getByte(value, i); + } + this.extensionValue = extValue; + DerValue val = new DerValue(extValue); + try { + names = new GeneralNames(val); + } catch (GeneralNamesException e) { + throw new IOException("IssuerAlternativeNameExtension" + + e.toString()); + } + } + + /** + * Returns a printable representation of the IssuerAlternativeName. + */ + public String toString() { + if (names == null) return ""; + String s = super.toString() + "IssuerAlternativeName [\n" + + names.toString() + "]\n"; + return (s); + } + + /** + * Decode the extension from the InputStream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception IOException on decoding or validity errors. + */ + public void decode(InputStream in) throws IOException { + throw new IOException("Method not to be called directly."); + } + + /** + * Write the extension to the OutputStream. + * + * @param out the OutputStream to write the extension to. + * @exception IOException on encoding error. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (extensionValue == null) { + extensionId = PKIXExtensions.IssuerAlternativeName_Id; + critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + clearValue(); + if (name.equalsIgnoreCase(ISSUER_NAME)) { + if (!(obj instanceof GeneralNames)) { + throw new IOException("Attribute value should be of" + + " type GeneralNames."); + } + names = (GeneralNames)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:IssuerAlternativeName."); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(ISSUER_NAME)) { + return (names); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:IssuerAlternativeName."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(ISSUER_NAME)) { + names = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:IssuerAlternativeName."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(ISSUER_NAME); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/pki/base/util/src/netscape/security/x509/IssuingDistributionPoint.java b/pki/base/util/src/netscape/security/x509/IssuingDistributionPoint.java new file mode 100644 index 00000000..50432240 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/IssuingDistributionPoint.java @@ -0,0 +1,311 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import org.mozilla.jss.asn1.*; +import netscape.security.util.*; +import java.io.*; + +/** + * <pre> + * issuingDistributionPoint ::= SEQUENCE { + * distributionPoint [0] DistributionPointName OPTIONAL, + * onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE, + * onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE, + * onlySomeReasons [3] ReasonFlags OPTIONAL, + * indirectCRL [4] BOOLEAN DEFAULT FALSE } + * + * DistributionPointName ::= CHOICE { + * fullName [0] GeneralNames, + * nameRelativeToCRLIssuer [1] RelativeDistinguishedName } + * + * ReasonFlags ::= BIT STRING { + * unused (0), + * keyCompromise (1), + * cACompromise (2), + * affiliationChanged (3), + * superseded (4), + * cessationOfOperation (5), + * certificateHold (6) } + * + * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + * + * GeneralName ::= CHOICE { + * otherName [0] OtherName, + * rfc822Name [1] IA5String, + * dNSName [2] IA5String, + * x400Address [3] ORAddress, + * directoryName [4] Name, + * ediPartyName [5] EDIPartyName, + * uniformResourceIdentifier [6] IA5String, + * iPAddress [7] OCTET STRING, + * registeredID [8] OBJECT IDENTIFIER} + * + * OtherName ::= SEQUENCE { + * type-id OBJECT IDENTIFIER, + * value [0] EXPLICIT ANY DEFINED BY type-id } + * + * EDIPartyName ::= SEQUENCE { + * nameAssigner [0] DirectoryString OPTIONAL, + * partyName [1] DirectoryString } + * + * RelativeDistinguishedName ::= + * SET OF AttributeTypeAndValue + * + * AttributeTypeAndValue ::= SEQUENCE { + * type AttributeType, + * value AttributeValue } + * + * AttributeType ::= OBJECT IDENTIFIER + * + * AttributeValue ::= ANY DEFINED BY AttributeType + * </pre> + * See the documentation in <code>CRLDistributionPoint</code> for + * the <code>DistributionPointName</code> and <code>ReasonFlags</code> + * ASN.1 types. + */ +public class IssuingDistributionPoint implements ASN1Value { + + // at most one of the following two may be specified. One or both can + // be null. + private GeneralNames fullName=null; + private RDN relativeName=null; + + private boolean onlyContainsUserCerts=false; // DEFAULT FALSE + private boolean onlyContainsCACerts=false; // DEFAULT FALSE + private BitArray onlySomeReasons=null; // optional, may be null + private boolean indirectCRL=false; // DEFAULT FALSE + + // cache encoding of fullName + private ANY fullNameEncoding; + + /** + * Returns the <code>fullName</code> of the + * <code>DistributionPointName</code>, which may be <code>null</code>. + */ + public GeneralNames getFullName() { + return fullName; + } + + /** + * Returns the <code>relativeName</code> of the + * <code>DistributionPointName</code>, which may be <code>null</code>. + */ + public RDN getRelativeName() { + return relativeName; + } + + /** + * Sets the <code>fullName</code> of the + * <code>DistributionPointName</code>. It may be set to <code>null</code>. + * If it is set to a non-null value, <code>relativeName</code> will be + * set to <code>null</code>, because at most one of these two attributes + * can be specified at a time. + * @exception GeneralNamesException If an error occurs encoding the + * name. + */ + public void setFullName(GeneralNames fullName) + throws GeneralNamesException, IOException + { + this.fullName = fullName; + if( fullName != null ) { + // encode the name to catch any problems with it + DerOutputStream derOut = new DerOutputStream(); + fullName.encode(derOut); + try { + ANY raw = new ANY(derOut.toByteArray()); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + raw.encodeWithAlternateTag( Tag.get(0), bos ); + fullNameEncoding = new ANY( bos.toByteArray() ); + } catch(InvalidBERException e) { + // assume this won't happen, since it would imply a bug + // in DerOutputStream + throw new GeneralNamesException( e.toString() ); + } + + this.relativeName = null; + } + } + + /** + * Sets the <code>relativeName</code> of the + * <code>DistributionPointName</code>. It may be set to <code>null</code>. + * If it is set to a non-null value, <code>fullName</code> will be + * set to <code>null</code>, because at most one of these two attributes + * can be specified at a time. + */ + public void setRelativeName(RDN relativeName) { + this.relativeName = relativeName; + if( relativeName != null ) { + this.fullName = null; + } + } + + public boolean getOnlyContainsUserCerts() { + return onlyContainsUserCerts; + } + + public void setOnlyContainsUserCerts(boolean b) { + onlyContainsUserCerts = b; + } + + public boolean getOnlyContainsCACerts() { + return onlyContainsCACerts; + } + + public void setOnlyContainsCACerts(boolean b) { + onlyContainsCACerts = b; + } + + /** + * Returns the reason flags for this distribution point. May be + * <code>null</code>. + */ + public BitArray getOnlySomeReasons() { + return onlySomeReasons; + } + + /** + * Sets the reason flags for this distribution point. May be set to + * <code>null</code>. + */ + public void setOnlySomeReasons(BitArray reasons) { + this.onlySomeReasons = reasons; + } + + public boolean getIndirectCRL() { + return indirectCRL; + } + + public void setIndirectCRL(boolean b) { + indirectCRL = b; + } + + + ///////////////////////////////////////////////////////////// + // DER encoding + ///////////////////////////////////////////////////////////// + private static final Tag TAG = SEQUENCE.TAG; + + public Tag getTag() { + return TAG; + } + + public void encode(OutputStream ostream) throws IOException { + encode(TAG, ostream); + } + + public void encode(Tag implicitTag, OutputStream ostream) + throws IOException + { + + SEQUENCE seq = new SEQUENCE(); + DerOutputStream derOut; + + try { + + // Encodes the DistributionPointName. Because DistributionPointName + // is a CHOICE, the [0] tag is forced to be EXPLICIT. + if( fullName != null ) { + EXPLICIT distPoint = new EXPLICIT( Tag.get(0), fullNameEncoding); + seq.addElement( distPoint ); + } else if( relativeName != null ) { + derOut = new DerOutputStream(); + relativeName.encode(derOut); + ANY raw = new ANY(derOut.toByteArray()); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + raw.encodeWithAlternateTag( Tag.get(1), bos ); + ANY distPointName = new ANY(bos.toByteArray()); + EXPLICIT distPoint = new EXPLICIT( Tag.get(0), distPointName); + seq.addElement( distPoint ); + } + + if( onlyContainsUserCerts != false ) { + seq.addElement( Tag.get(1), new BOOLEAN(true)); + } + if( onlyContainsCACerts != false ) { + seq.addElement( Tag.get(2), new BOOLEAN(true)); + } + + // Encodes the ReasonFlags. + if( onlySomeReasons != null ) { + derOut = new DerOutputStream(); + derOut.putUnalignedBitString(onlySomeReasons); + ANY raw = new ANY(derOut.toByteArray()); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + raw.encodeWithAlternateTag(Tag.get(3), bos); + ANY reasonEncoding = new ANY(bos.toByteArray()); + seq.addElement( reasonEncoding); + } + + if( indirectCRL != false ) { + seq.addElement( Tag.get(4), new BOOLEAN(true)); + } + + seq.encode(implicitTag, ostream); + + } catch(InvalidBERException e) { + // this shouldn't happen unless there is a bug in one of + // the Sun encoding classes + throw new IOException(e.toString()); + } + } + + public static void main(String args[]) { + + try { + if(args.length != 1) { + System.out.println("Usage: IssuingDistributionPoint <outfile>"); + System.exit(-1); + } + + BufferedOutputStream bos = new BufferedOutputStream( + new FileOutputStream(args[0])); + + SEQUENCE idps = new SEQUENCE(); + + IssuingDistributionPoint idp = new IssuingDistributionPoint(); + + X500Name dn = new X500Name("CN=Skovw Wjasldk,E=nicolson@netscape.com"+ + ",OU=Certificate Server,O=Netscape,C=US"); + GeneralNames generalNames = new GeneralNames(); + generalNames.addElement(dn); + idp.setFullName(generalNames); + idps.addElement(idp); + + idp = new IssuingDistributionPoint(); + URIName uri = new URIName("http://www.mycrl.com/go/here"); + generalNames = new GeneralNames(); + generalNames.addElement(uri); + idp.setFullName(generalNames); + idp.setOnlyContainsUserCerts(true); + idp.setOnlyContainsCACerts(true); + idp.setIndirectCRL(true); + BitArray ba = new BitArray(5, new byte[] {(byte)0x28} ); + idp.setOnlySomeReasons(ba); + idps.addElement(idp); + + idps.encode(bos); + bos.close(); + } catch(Exception e) { + e.printStackTrace(); + } + } + +} + diff --git a/pki/base/util/src/netscape/security/x509/IssuingDistributionPointExtension.java b/pki/base/util/src/netscape/security/x509/IssuingDistributionPointExtension.java new file mode 100644 index 00000000..6ee8dffb --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/IssuingDistributionPointExtension.java @@ -0,0 +1,414 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import netscape.security.util.*; +import netscape.security.x509.*; +import java.io.*; +import org.mozilla.jss.asn1.*; +import java.security.cert.*; +import java.util.Vector; +import java.util.Enumeration; + +/** + * A critical CRL extension that identifies the CRL distribution point + * for a particular CRL + * + * <pre> + * issuingDistributionPoint ::= SEQUENCE { + * distributionPoint [0] DistributionPointName OPTIONAL, + * onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE, + * onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE, + * onlySomeReasons [3] ReasonFlags OPTIONAL, + * indirectCRL [4] BOOLEAN DEFAULT FALSE } + * + * DistributionPointName ::= CHOICE { + * fullName [0] GeneralNames, + * nameRelativeToCRLIssuer [1] RelativeDistinguishedName } + * + * ReasonFlags ::= BIT STRING { + * unused (0), + * keyCompromise (1), + * cACompromise (2), + * affiliationChanged (3), + * superseded (4), + * cessationOfOperation (5), + * certificateHold (6) } + * + * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + * + * GeneralName ::= CHOICE { + * otherName [0] OtherName, + * rfc822Name [1] IA5String, + * dNSName [2] IA5String, + * x400Address [3] ORAddress, + * directoryName [4] Name, + * ediPartyName [5] EDIPartyName, + * uniformResourceIdentifier [6] IA5String, + * iPAddress [7] OCTET STRING, + * registeredID [8] OBJECT IDENTIFIER} + * + * OtherName ::= SEQUENCE { + * type-id OBJECT IDENTIFIER, + * value [0] EXPLICIT ANY DEFINED BY type-id } + * + * EDIPartyName ::= SEQUENCE { + * nameAssigner [0] DirectoryString OPTIONAL, + * partyName [1] DirectoryString } + * + * RelativeDistinguishedName ::= + * SET OF AttributeTypeAndValue + * + * AttributeTypeAndValue ::= SEQUENCE { + * type AttributeType, + * value AttributeValue } + * + * AttributeType ::= OBJECT IDENTIFIER + * + * AttributeValue ::= ANY DEFINED BY AttributeType + * </pre> + */ +public class IssuingDistributionPointExtension extends Extension + implements CertAttrSet +{ + /** + * The Object Identifier for this extension. + */ + public static final String OID = "2.5.29.28"; + + /** + * Attribute names. + */ + public static final String NAME = "IssuingDistributionPoint"; + public static final String ISSUING_DISTRIBUTION_POINT = "issuing_distribution_point"; + + // Private data members + private IssuingDistributionPoint issuingDistributionPoint = null; + + // Cached DER-encoding to improve performance. + private byte[] cachedEncoding=null; + + + // no default constructor + private IssuingDistributionPointExtension() { } + + + static { + try { + OIDMap.addAttribute(IssuingDistributionPointExtension.class.getName(), + OID, NAME); + } catch (CertificateException e) {} + } + + + /** + * This constructor is very important, since it will be called + * by the system. + */ + public IssuingDistributionPointExtension(Boolean critical, Object value) + throws IOException { + + this.extensionId = PKIXExtensions.IssuingDistributionPoint_Id; + this.critical = critical.booleanValue(); + this.extensionValue = (byte[])((byte[])value).clone(); + + byte[] extValue = this.extensionValue; + issuingDistributionPoint = new IssuingDistributionPoint(); + DerValue val = new DerValue(extValue); + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding of IssuingDistributionPoint"); + } + + while (val.data.available() != 0) { + DerValue opt = val.data.getDerValue(); + + if (opt != null) { + for (int i = 0; i < 5; i++) { + if (opt.isContextSpecific((byte)i)) { + if ((i == 0 && opt.isConstructed() && opt.data.available() != 0) || + (i != 0 && (!opt.isConstructed()) && opt.data.available() != 0)) { + + if (i == 0) { + DerValue opt1 = opt.data.getDerValue(); + if (opt1 != null) { + if (opt1.isContextSpecific((byte)0)) { + if (opt1.isConstructed() && opt1.data.available() != 0) { + opt1.resetTag(DerValue.tag_Sequence); + + try { + GeneralNames fullName = new GeneralNames(opt1); + if (fullName != null) { + issuingDistributionPoint.setFullName(fullName); + } + } catch (GeneralNamesException e) { + throw new IOException("Invalid encoding of IssuingDistributionPoint "+e); + } catch (IOException e) { + throw new IOException("Invalid encoding of IssuingDistributionPoint "+e); + } + } else { + throw new IOException("Invalid encoding of IssuingDistributionPoint"); + } + + } else if (opt1.isContextSpecific((byte)1)) { + if (opt1.isConstructed() && opt1.data.available() != 0) { + opt1.resetTag(DerValue.tag_Set); + + try { + RDN relativeName = new RDN(opt1); + if (relativeName != null) { + issuingDistributionPoint.setRelativeName(relativeName); + } + } catch (IOException e) { + throw new IOException("Invalid encoding of IssuingDistributionPoint "+e); + } + } else { + throw new IOException("Invalid encoding of IssuingDistributionPoint"); + } + } + } + + } else if (i == 3) { + opt.resetTag(DerValue.tag_BitString); + try { + BitArray reasons = opt.getUnalignedBitString(); + issuingDistributionPoint.setOnlySomeReasons(reasons); + byte[] a = reasons.toByteArray(); + } catch (IOException e) { + throw new IOException("Invalid encoding of IssuingDistributionPoint "+e); + } + + } else { + opt.resetTag(DerValue.tag_Boolean); + try { + boolean b = opt.getBoolean(); + if (i == 1) { + issuingDistributionPoint.setOnlyContainsUserCerts(b); + } else if (i == 2) { + issuingDistributionPoint.setOnlyContainsCACerts(b); + } else if (i == 4) { + issuingDistributionPoint.setIndirectCRL(b); + } + } catch (IOException e) { + throw new IOException("Invalid encoding of IssuingDistributionPoint "+e); + } + } + } else { + throw new IOException("Invalid encoding of IssuingDistributionPoint"); + } + } + } + } else { + throw new IOException("Invalid encoding of IssuingDistributionPoint"); + } + } + + } + + + /** + * Creates a new IssuingDistributionPoint extension, with the given + * issuing distribution point as the first element. + */ + public IssuingDistributionPointExtension(IssuingDistributionPoint idp) { + this.extensionId = PKIXExtensions.IssuingDistributionPoint_Id; + this.critical = true; + issuingDistributionPoint = idp; + } + + /** + * Returns the issuing distribution point. + */ + public IssuingDistributionPoint getIssuingDistributionPoint() { + return issuingDistributionPoint; + } + + /** + * Sets the criticality of this extension. PKIX dictates that this + * extension SHOULD be critical, so applications can make it not critical + * if they have a very good reason. By default, the extension is critical. + */ + public void setCritical(boolean critical) { + this.critical = critical; + } + + /** + * Gets the criticality of this extension. PKIX dictates that this + * extension SHOULD be critical, so by default, the extension is critical. + */ + public boolean getCritical(boolean critical) { + return this.critical; + } + + /** + * Encodes this extension to the given DerOutputStream. + * This method re-encodes each time it is called, so it is not very + * efficient. + */ + public void encode(DerOutputStream out) throws IOException { + extensionValue = ASN1Util.encode(issuingDistributionPoint); + super.encode(out); + } + + /** + * Should be called if any change is made to this data structure + * so that the cached DER encoding can be discarded. + */ + public void flushCachedEncoding() { + cachedEncoding = null; + } + + /** + * Returns a printable representation of the IssuingDistributionPointExtension + */ + + public String toString() { + return NAME; + } + + /** + * DER-encodes this extension to the given OutputStream. + */ + public void encode(OutputStream ostream) + throws CertificateException, IOException + { + if( cachedEncoding == null ) { + // only re-encode if necessary + DerOutputStream tmp = new DerOutputStream(); + encode(tmp); + cachedEncoding = tmp.toByteArray(); + } + ostream.write(cachedEncoding); + } + + public void decode(InputStream in) + throws CertificateException, IOException + { + throw new IOException("Not supported"); + } + + public void set(String name, Object obj) + throws CertificateException, IOException + { + if (name.equalsIgnoreCase(ISSUING_DISTRIBUTION_POINT)) { + if (!(obj instanceof IssuingDistributionPoint)) { + throw new IOException("Attribute value should be of type IssuingDistributionPoint."); + } + issuingDistributionPoint = (IssuingDistributionPoint)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:IssuingDistributionPointExtension"); + } + } + + public Object get(String name) + throws CertificateException, IOException + { + if (name.equalsIgnoreCase(ISSUING_DISTRIBUTION_POINT)) { + return issuingDistributionPoint; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:IssuingDistributionPointExtension"); + } + } + + public void delete(String name) + throws CertificateException, IOException + { + if (name.equalsIgnoreCase(ISSUING_DISTRIBUTION_POINT)) { + issuingDistributionPoint = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:IssuingDistributionPointExtension"); + } + } + + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(ISSUING_DISTRIBUTION_POINT); + return (elements.elements()); +// return (new Vector()).elements(); + } + + + public String getName() { + return NAME; + } + + /** + * Test driver. + */ + public static void main(String args[]) { + + try { + + if( args.length != 1 ) { + System.out.println("Usage: IssuingDistributionPointExtension "+ + "<outfile>"); + System.exit(-1); + } + + BufferedOutputStream bos = new BufferedOutputStream( + new FileOutputStream(args[0]) ); + + + // URI only + IssuingDistributionPoint idp = new IssuingDistributionPoint(); + URIName uri = new URIName("http://www.mycrl.com/go/here"); + GeneralNames generalNames = new GeneralNames(); + generalNames.addElement(uri); + idp.setFullName(generalNames); + IssuingDistributionPointExtension idpExt = + new IssuingDistributionPointExtension(idp); + + // DN only + idp = new IssuingDistributionPoint(); + X500Name dn = new X500Name("CN=Otis Smith,E=otis@fedoraproject.org"+ + ",OU=Certificate Server,O=Fedora,C=US"); + generalNames = new GeneralNames(); + generalNames.addElement(dn); + idp.setFullName(generalNames); + idpExt.set(idpExt.ISSUING_DISTRIBUTION_POINT, idp); + + // DN + reason + BitArray ba = new BitArray(5, new byte[] {(byte)0x28} ); + idp = new IssuingDistributionPoint(); + idp.setFullName(generalNames); + idp.setOnlySomeReasons(ba); + idpExt.set(idpExt.ISSUING_DISTRIBUTION_POINT, idp); + + + // relative DN + reason + crlIssuer + idp = new IssuingDistributionPoint(); + RDN rdn = new RDN("OU=foobar dept"); + idp.setRelativeName(rdn); + idp.setOnlySomeReasons(ba); + idp.setOnlyContainsCACerts(true); + idp.setOnlyContainsUserCerts(true); + idp.setIndirectCRL(true); + idpExt.set(idpExt.ISSUING_DISTRIBUTION_POINT, idp); + + idpExt.setCritical(false); + idpExt.encode(bos); + + bos.close(); + + } catch(Exception e) { + e.printStackTrace(); + } + } +} diff --git a/pki/base/util/src/netscape/security/x509/KeyIdentifier.java b/pki/base/util/src/netscape/security/x509/KeyIdentifier.java new file mode 100644 index 00000000..1aa39fc4 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/KeyIdentifier.java @@ -0,0 +1,82 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; + +import netscape.security.util.*; + +/** + * Represent the Key Identifier ASN.1 object. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.4 + */ +public class KeyIdentifier implements java.io.Serializable { + private byte[] octetString; + + /** + * Create a KeyIdentifier with the passed bit settings. + * + * @param octetString the octet string identifying the key identifier. + */ + public KeyIdentifier(byte[] octetString) { + this.octetString = octetString; + } + + /** + * Create a KeyIdentifier from the DER encoded value. + * + * @param val the DerValue + */ + public KeyIdentifier(DerValue val) throws IOException { + octetString = val.getOctetString(); + } + + /** + * Return the value of the KeyIdentifier as byte array. + */ + public byte[] getIdentifier() { + return ((byte[])octetString.clone()); + } + + /** + * Returns a printable representation of the KeyUsage. + */ + public String toString() { + netscape.security.util.PrettyPrintFormat pp = + new netscape.security.util.PrettyPrintFormat(" ", 20); + String octetbits = pp.toHexString(octetString); + + String s = "KeyIdentifier [\n"; + s += octetbits; + s += "]\n"; + return (s); + } + + /** + * Write the KeyIdentifier to the DerOutputStream. + * + * @param out the DerOutputStream to write the object to. + * @exception IOException + */ + void encode (DerOutputStream out) throws IOException { + out.putOctetString(octetString); + } +} diff --git a/pki/base/util/src/netscape/security/x509/KeyUsageExtension.java b/pki/base/util/src/netscape/security/x509/KeyUsageExtension.java new file mode 100644 index 00000000..1d6605ae --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/KeyUsageExtension.java @@ -0,0 +1,408 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.util.Enumeration; + +import netscape.security.util.*; + +/** + * Represent the Key Usage Extension. + * + * <p>This extension, if present, defines the purpose (e.g., encipherment, + * signature, certificate signing) of the key contained in the certificate. + * The usage restriction might be employed when a multipurpose key is to be + * restricted (e.g., when an RSA key should be used only for signing or only + * for key encipherment). + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.9 + * @see Extension + * @see CertAttrSet + */ +public class KeyUsageExtension extends Extension +implements CertAttrSet { + + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.extensions.KeyUsage"; + /** + * Attribute names. + */ + public static final String NAME = "KeyUsage"; + public static final String DIGITAL_SIGNATURE = "digital_signature"; + public static final String NON_REPUDIATION = "non_repudiation"; + public static final String KEY_ENCIPHERMENT = "key_encipherment"; + public static final String DATA_ENCIPHERMENT = "data_encipherment"; + public static final String KEY_AGREEMENT = "key_agreement"; + public static final String KEY_CERTSIGN = "key_certsign"; + public static final String CRL_SIGN = "crl_sign"; + public static final String ENCIPHER_ONLY = "encipher_only"; + public static final String DECIPHER_ONLY = "decipher_only"; + + public static final int DIGITAL_SIGNATURE_BIT = 0; + public static final int NON_REPUDIATION_BIT = 1; + public static final int KEY_ENCIPHERMENT_BIT = 2; + public static final int DATA_ENCIPHERMENT_BIT = 3; + public static final int KEY_AGREEMENT_BIT = 4; + public static final int KEY_CERTSIGN_BIT = 5; + public static final int CRL_SIGN_BIT = 6; + public static final int ENCIPHER_ONLY_BIT = 7; + public static final int DECIPHER_ONLY_BIT = 8; + + public static final int NBITS = 9; + + public static String[] names = new String[NBITS]; + + static { + names[DIGITAL_SIGNATURE_BIT] = DIGITAL_SIGNATURE; + names[NON_REPUDIATION_BIT] = NON_REPUDIATION; + names[KEY_ENCIPHERMENT_BIT] = KEY_ENCIPHERMENT; + names[DATA_ENCIPHERMENT_BIT] = DATA_ENCIPHERMENT; + names[KEY_AGREEMENT_BIT] = KEY_AGREEMENT; + names[KEY_CERTSIGN_BIT] = KEY_CERTSIGN; + names[CRL_SIGN_BIT] = CRL_SIGN; + names[ENCIPHER_ONLY_BIT] = ENCIPHER_ONLY; + names[DECIPHER_ONLY_BIT] = DECIPHER_ONLY; + } + + // Private data members + private boolean[] bitString; + + // Encode this extension value + private void encodeThis() throws IOException { + DerOutputStream os = new DerOutputStream(); + os.putUnalignedBitString(this.bitString); + this.extensionValue = os.toByteArray(); + } + + /** + * Check if bit is set. + * + * @param position the position in the bit string to check. + */ + private boolean isSet(int position) { + if (bitString.length <= position) + return false; + return bitString[position]; + } + + /** + * Set the bit at the specified position. + */ + private void set(int position, boolean val) { + // enlarge bitString if necessary + if (position >= bitString.length) { + boolean[] tmp = new boolean[position+1]; + System.arraycopy(bitString, 0, tmp, 0, bitString.length); + bitString = tmp; + } + bitString[position] = val; + } + + /** + * Create a KeyUsageExtension with the passed bit settings. The criticality + * is set to true. + * + * @param bitString the bits to be set for the extension. + */ + public KeyUsageExtension(boolean critical, byte[] bitString) throws IOException { + this.bitString = + new BitArray(bitString.length*8,bitString).toBooleanArray(); + this.extensionId = PKIXExtensions.KeyUsage_Id; + this.critical = critical; + encodeThis(); + } + + public KeyUsageExtension(byte[] bitString) throws IOException { + this.bitString = + new BitArray(bitString.length*8,bitString).toBooleanArray(); + this.extensionId = PKIXExtensions.KeyUsage_Id; + this.critical = true; + encodeThis(); + } + + /** + * Create a KeyUsageExtension with the passed bit settings. The criticality + * is set to true. + * + * @param bitString the bits to be set for the extension. + */ + public KeyUsageExtension(boolean critical, boolean[] bitString) throws IOException { + this.bitString = bitString; + this.extensionId = PKIXExtensions.KeyUsage_Id; + this.critical = critical; + encodeThis(); + } + + public KeyUsageExtension(boolean[] bitString) throws IOException { + this.bitString = bitString; + this.extensionId = PKIXExtensions.KeyUsage_Id; + this.critical = true; + encodeThis(); + } + + /** + * Create a KeyUsageExtension with the passed bit settings. The criticality + * is set to true. + * + * @param bitString the bits to be set for the extension. + */ + public KeyUsageExtension(BitArray bitString) throws IOException { + this.bitString = bitString.toBooleanArray(); + this.extensionId = PKIXExtensions.KeyUsage_Id; + this.critical = true; + encodeThis(); + } + + /** + * Create the extension from the passed DER encoded value of the same. + * + * @param critical true if the extension is to be treated as critical. + * @param value Array of DER encoded bytes of the actual value. + * @exception IOException on error. + */ + public KeyUsageExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.KeyUsage_Id; + this.critical = critical.booleanValue(); + /* + * The following check should be activated again after + * the PKIX profiling work becomes standard and the check + * is not a barrier to interoperability ! + * if (!this.critical) { + * throw new IOException("KeyUsageExtension not marked critical," + * + " invalid profile."); + * } + */ + int len = Array.getLength(value); + byte[] extValue = new byte[len]; + for (int i = 0; i < len; i++) { + extValue[i] = Array.getByte(value, i); + } + this.extensionValue = extValue; + DerValue val = new DerValue(extValue); + this.bitString = val.getUnalignedBitString().toBooleanArray(); + } + + /** + * Create a default key usage. + */ + public KeyUsageExtension() { + extensionId = PKIXExtensions.KeyUsage_Id; + critical = true; + bitString = new boolean[0]; + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + clearValue(); + if (!(obj instanceof Boolean)) { + throw new IOException("Attribute must be of type Boolean."); + } + boolean val = ((Boolean)obj).booleanValue(); + if (name.equalsIgnoreCase(DIGITAL_SIGNATURE)) { + set(0,val); + } else if (name.equalsIgnoreCase(NON_REPUDIATION)) { + set(1,val); + } else if (name.equalsIgnoreCase(KEY_ENCIPHERMENT)) { + set(2,val); + } else if (name.equalsIgnoreCase(DATA_ENCIPHERMENT)) { + set(3,val); + } else if (name.equalsIgnoreCase(KEY_AGREEMENT)) { + set(4,val); + } else if (name.equalsIgnoreCase(KEY_CERTSIGN)) { + set(5,val); + } else if (name.equalsIgnoreCase(CRL_SIGN)) { + set(6,val); + } else if (name.equalsIgnoreCase(ENCIPHER_ONLY)) { + set(7,val); + } else if (name.equalsIgnoreCase(DECIPHER_ONLY)) { + set(8,val); + } else { + throw new IOException("Attribute name not recognized by" + + " CertAttrSet:KeyUsage."); + } + encodeThis(); + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(DIGITAL_SIGNATURE)) { + return new Boolean(isSet(0)); + } else if (name.equalsIgnoreCase(NON_REPUDIATION)) { + return new Boolean(isSet(1)); + } else if (name.equalsIgnoreCase(KEY_ENCIPHERMENT)) { + return new Boolean(isSet(2)); + } else if (name.equalsIgnoreCase(DATA_ENCIPHERMENT)) { + return new Boolean(isSet(3)); + } else if (name.equalsIgnoreCase(KEY_AGREEMENT)) { + return new Boolean(isSet(4)); + } else if (name.equalsIgnoreCase(KEY_CERTSIGN)) { + return new Boolean(isSet(5)); + } else if (name.equalsIgnoreCase(CRL_SIGN)) { + return new Boolean(isSet(6)); + } else if (name.equalsIgnoreCase(ENCIPHER_ONLY)) { + return new Boolean(isSet(7)); + } else if (name.equalsIgnoreCase(DECIPHER_ONLY)) { + return new Boolean(isSet(8)); + } else { + throw new IOException("Attribute name not recognized by" + + " CertAttrSet:KeyUsage."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(DIGITAL_SIGNATURE)) { + set(0,false); + } else if (name.equalsIgnoreCase(NON_REPUDIATION)) { + set(1,false); + } else if (name.equalsIgnoreCase(KEY_ENCIPHERMENT)) { + set(2,false); + } else if (name.equalsIgnoreCase(DATA_ENCIPHERMENT)) { + set(3,false); + } else if (name.equalsIgnoreCase(KEY_AGREEMENT)) { + set(4,false); + } else if (name.equalsIgnoreCase(KEY_CERTSIGN)) { + set(5,false); + } else if (name.equalsIgnoreCase(CRL_SIGN)) { + set(6,false); + } else if (name.equalsIgnoreCase(ENCIPHER_ONLY)) { + set(7,false); + } else if (name.equalsIgnoreCase(DECIPHER_ONLY)) { + set(8,false); + } else { + throw new IOException("Attribute name not recognized by" + + " CertAttrSet:KeyUsage."); + } + } + + /** + * Returns a printable representation of the KeyUsage. + */ + public String toString() { + String s = super.toString() + "KeyUsage [\n"; + + try { + if (isSet(0)) { + s += " DigitalSignature\n"; + } + if (isSet(1)) { + s += " Non_repudiation\n"; + } + if (isSet(2)) { + s += " Key_Encipherment\n"; + } + if (isSet(3)) { + s += " Data_Encipherment\n"; + } + if (isSet(4)) { + s += " Key_Agreement\n"; + } + if (isSet(5)) { + s += " Key_CertSign\n"; + } + if (isSet(6)) { + s += " Crl_Sign\n"; + } + if (isSet(7)) { + s += " Encipher_Only\n"; + } + if (isSet(8)) { + s += " Decipher_Only\n"; + } + } catch (ArrayIndexOutOfBoundsException ex) {} + + s += "]\n"; + + return (s); + } + + /** + * Decode the extension from the InputStream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception IOException on decoding or validity errors. + */ + public void decode(InputStream in) throws IOException { + throw new IOException("Method not to be called directly."); + } + + /** + * Write the extension to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + + if (this.extensionValue == null) { + this.extensionId = PKIXExtensions.KeyUsage_Id; + this.critical = true; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements () { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(DIGITAL_SIGNATURE); + elements.addElement(NON_REPUDIATION); + elements.addElement(KEY_ENCIPHERMENT); + elements.addElement(DATA_ENCIPHERMENT); + elements.addElement(KEY_AGREEMENT); + elements.addElement(KEY_CERTSIGN); + elements.addElement(CRL_SIGN); + elements.addElement(ENCIPHER_ONLY); + elements.addElement(DECIPHER_ONLY); + + return (elements.elements()); + } + + + public boolean[] getBits() { + return (boolean[]) bitString.clone(); + } + + /** + * Return the name of this attribute. + */ + public String getName () { + return (NAME); + } +} diff --git a/pki/base/util/src/netscape/security/x509/LdapDNStrConverter.java b/pki/base/util/src/netscape/security/x509/LdapDNStrConverter.java new file mode 100644 index 00000000..ee93581c --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/LdapDNStrConverter.java @@ -0,0 +1,150 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; + +import netscape.security.util.*; + +/** + * Abstract class that converts a Ldap DN String to an X500Name, RDN or AVA + * and vice versa, except the string is a java string in unicode. + * + * @author Lily Hsiao, Slava Galperin at Netscape Communications, Inc. + */ + +public abstract class LdapDNStrConverter +{ + // + // public parsing methods. + // + + /** + * Converts a Ldap DN string to a X500Name object. + * + * @param dn a Ldap DN String. + * + * @return an X500Name object for the Ldap DN String. + */ + public abstract X500Name parseDN(String dn) + throws IOException; + + /** + * Like parseDN with a specified DER encoding order for Directory Strings. + */ + public abstract X500Name parseDN(String dn, byte[] tags) + throws IOException; + + /** + * Converts a Ldap DN string to a RDN object. + * + * @param rdn a Ldap DN String + * + * @return an RDN object. + */ + public abstract RDN parseRDN(String rdn) + throws IOException; + + /** + * Like parseRDN with a specified DER encoding order for Directory Strings. + */ + public abstract RDN parseRDN(String rdn, byte[] tags) + throws IOException; + + /** + * Converts a Ldap DN string to a AVA object. + * + * @param ava a Ldap DN string. + * @return an AVA object. + */ + public abstract AVA parseAVA(String ava) + throws IOException; + + /** + * Like parseAVA with a specified DER encoding order for Directory Strings. + */ + public abstract AVA parseAVA(String rdn, byte[] tags) + throws IOException; + + // + // public encoding methods. + // + + /** + * Converts a X500Name object to a Ldap dn string. + * + * @param dn an X500Name object. + * @return a Ldap DN String. + */ + public abstract String encodeDN(X500Name dn) throws IOException; + + /** + * Converts an RDN object to a Ldap dn string. + * + * @param rdn an RDN object. + * @return a Ldap dn string. + */ + public abstract String encodeRDN(RDN rdn) throws IOException; + + /** + * Converts an AVA object to a Ldap dn string. + * + * @param ava An AVA object. + * @return A Ldap dn string. + */ + public abstract String encodeAVA(AVA ava) throws IOException; + + // + // public static methods + // + + /** + * Gets a global default Ldap DN String converter. + * Currently it is LdapV3DNStrConverter object using the default + * X500NameAttrMap and accepts unknown OIDs. + * + * @see netscape.security.x509.LdapV3DNStrConverter + * + * @return The global default LdapDNStrConverter instance. + */ + public static LdapDNStrConverter getDefault() + { + return defaultConverter; + } + + /** + * Set the global default LdapDNStrConverter object. + * + * @param defConverter A LdapDNStrConverter object to become + * the global default. + */ + public static void setDefault(LdapDNStrConverter defConverter) + { + if (defConverter == null) + throw new IllegalArgumentException( + "The default Ldap DN String converter cannot be set to null."); + defaultConverter = defConverter; + } + + // + // private static variables + // + + private static LdapDNStrConverter + defaultConverter = new LdapV3DNStrConverter(); +} diff --git a/pki/base/util/src/netscape/security/x509/LdapV3DNStrConverter.java b/pki/base/util/src/netscape/security/x509/LdapV3DNStrConverter.java new file mode 100644 index 00000000..68deca82 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/LdapV3DNStrConverter.java @@ -0,0 +1,869 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.*; +import java.util.*; +import sun.io.ByteToCharConverter; +import netscape.security.util.*; + +/** + * A converter that converts Ldap v3 DN strings as specified in + * draft-ietf-asid-ldapv3-dn-03.txt to a X500Name, RDN or AVA and + * vice versa. + * + * @see LdapDNStrConverter + * @see X500Name + * @see RDN + * @see AVA + * @see X500NameAttrMap + * + * @author Lily Hsiao, Slava Galperin at Netscape Communications, Inc. + */ + + +public class LdapV3DNStrConverter extends LdapDNStrConverter +{ + // + // Constructors + // + + /** + * Constructs a LdapV3DNStrConverter using the global default + * X500NameAttrMap and accept OIDs not in the default X500NameAttrMap. + * @see X500NameAttrMap + */ + public LdapV3DNStrConverter() + { + attrMap = X500NameAttrMap.getDefault(); + + + acceptUnknownOids = true; + } + + /** + * Constructs a LdapV3DNStrConverter using the specified X500NameAttrMap + * and a boolean indicating whether to accept OIDs not listed in the + * X500NameAttrMap. + * + * @param attributeMap a X500NameAttrMap + * @param doAcceptUnknownOids whether to convert unregistered OIDs + * (oids not in the X500NameAttrMap) + * @see X500NameAttrMap + */ + public LdapV3DNStrConverter(X500NameAttrMap attributeMap, + boolean doAcceptUnknownOids) + { + attrMap = attributeMap; + acceptUnknownOids = doAcceptUnknownOids; + + } + + // + // public parsing methods + // From LdapDNStrConverter interface + // + + /** + * Parse a Ldap v3 DN string to a X500Name. + * + * @param dn a LDAP v3 DN String + * @return a X500Name + * @exception IOException if an error occurs during the conversion. + */ + public X500Name parseDN(String dn) + throws IOException + { + return parseDN(dn, null); + } + + /** + * Like parseDN(String) with a DER encoding order given as argument for + * Directory Strings. + */ + public X500Name parseDN(String dn, byte[] encodingOrder) + throws IOException + { + StringReader dn_reader = new StringReader(dn); + PushbackReader in = new PushbackReader(dn_reader, 5); + Vector rdnVector = new Vector(); + RDN[] names; + + return parseDN(in, encodingOrder); + } + + /** + * Parse a Ldap v3 DN string with a RDN component to a RDN + * + * @param rdn a LDAP v3 DN String + * @return a RDN + * @exception IOException if an error occurs during the conversion. + */ + public RDN parseRDN(String rdn) + throws IOException + { + return parseRDN(rdn, null); + } + + /** + * Like parseRDN(String) with a DER encoding order given as argument for + * Directory Strings. + */ + public RDN parseRDN(String rdn, byte[] encodingOrder) + throws IOException + { + StringReader rdn_reader = new StringReader(rdn); + PushbackReader in = new PushbackReader(rdn_reader, 5); + Vector avaVector = new Vector(); + AVA[] assertion; + + return parseRDN(in, null); + } + + /** + * Parse a Ldap v3 DN string with a AVA component to a AVA. + * + * @param ava a LDAP v3 DN string + * @return a AVA + */ + public AVA parseAVA(String ava) + throws IOException + { + return parseAVA(ava, null); + } + + /** + * Like parseDN(String) with a DER encoding order given as argument for + * Directory Strings. + */ + public AVA parseAVA(String ava, byte[] encodingOrder) + throws IOException + { + StringReader ava_reader = new StringReader(ava); + PushbackReader in = new PushbackReader(ava_reader, 5); + + return parseAVA(in, encodingOrder); + } + + + // + // public parsing methods called by other methods. + // + + /** + * Parses a Ldap DN string in a string reader to a X500Name. + * + * @param in Pushback string reader for a Ldap DN string. + * The pushback reader must have a pushback buffer size > 2. + * + * @return a X500Name + * + * @exception IOException if any reading or parsing error occurs. + */ + public X500Name parseDN(PushbackReader in) + throws IOException + { + return parseDN(in, null); + } + + /** + * Like parseDN(PushbackReader in) with a DER encoding order given as + * argument for Directory Strings. + */ + public X500Name parseDN(PushbackReader in, byte[] encodingOrder) + throws IOException + { + RDN rdn; + int lastChar; + Vector rdnVector = new Vector(); + RDN names[]; + int i,j; + + do { + rdn = parseRDN(in, encodingOrder); + rdnVector.addElement(rdn); + lastChar = in.read(); + } + while (lastChar == ',' || lastChar == ';'); + + names = new RDN[rdnVector.size()]; + for (i=0, j=rdnVector.size()-1; i < rdnVector.size(); i++, j--) + names[j] = (RDN)rdnVector.elementAt(i); + return new X500Name(names); + } + + /** + * Parses Ldap DN string with a rdn component + * from a string reader to a RDN. The string reader will point + * to the separator after the rdn component or -1 if at end of string. + * + * @param in Pushback string reader containing a Ldap DN string with + * at least one rdn component. + * The pushback reader must have a pushback buffer size > 2. + * + * @return RDN object of the first rdn component in the Ldap DN string. + * + * @exception IOException if any read or parse error occurs. + */ + public RDN parseRDN(PushbackReader in) + throws IOException + { + return parseRDN(in, null); + } + + /** + * Like parseRDN(PushbackReader) with a DER encoding order given as + * argument for Directory Strings. + */ + public RDN parseRDN(PushbackReader in, byte[] encodingOrder) + throws IOException + { + Vector avaVector = new Vector(); + AVA ava; + int lastChar; + AVA assertion[]; + + do { + ava = parseAVA(in, encodingOrder); + avaVector.addElement(ava); + lastChar = in.read(); + } + while (lastChar == '+' ); + + if (lastChar != -1) + in.unread(lastChar); + + assertion = new AVA[avaVector.size()]; + for (int i = 0; i < avaVector.size(); i++) + assertion[i] = (AVA)avaVector.elementAt(i); + return new RDN(assertion); + } + + /** + * Parses a Ldap DN string with a AVA component + * from a string reader to an AVA. The string reader will point + * to the AVA separator after the ava string or -1 if end of string. + * + * @param in a Pushback reader containg a Ldap string with + * at least one AVA component. + * The Pushback reader must have a pushback buffer size > 2. + * + * @return AVA object of the first AVA component in the Ldap DN string. + */ + public AVA parseAVA(PushbackReader in) + throws IOException + { + return parseAVA(in, null); + } + + /** + * Like parseAVA(PushbackReader) with a DER encoding order given as + * argument for Directory Strings. + */ + public AVA parseAVA(PushbackReader in, byte[] encodingOrder) + throws IOException + { + int c; + ObjectIdentifier oid; + DerValue value; + StringBuffer keywordBuf; + StringBuffer valueBuf; + ByteArrayOutputStream berStream; + char hexChar1, hexChar2; + CharArrayWriter hexCharsBuf; + String endChars; + + /* First get the keyword indicating the attribute's type, + * and map it to the appropriate OID. + */ + keywordBuf = new StringBuffer(); + for (;;) { + c = in.read (); + if (c == '=') + break; + if (c == -1) + { + throw new IOException("Bad AVA format: Missing '='"); + } + keywordBuf.append ((char)c); + } + oid = parseAVAKeyword(keywordBuf.toString()); + + /* Now parse the value. "#hex", a quoted string, or a string + * terminated by "+", ",", ";", ">". Whitespace before or after + * the value is stripped. + */ + for (c = in.read (); c == ' '; c = in.read ()) + continue; + if (c == -1) + throw new IOException("Bad AVA format: Missing attribute value"); + + if (c == '#') + { + /* + * NOTE per LDAPv3 dn string ietf standard the value represented + * by this form is a BER value. But we only support DER value here + * which is only a form of BER. + */ + berStream = new ByteArrayOutputStream(); + int b; + for (;;) { + hexChar1 = (char)(c = in.read()); + if (c == -1 || octoEndChars.indexOf(c) > 0) // end of value + break; + hexChar2 = (char)(c = in.read()); + if (hexDigits.indexOf(hexChar1) == -1 || + hexDigits.indexOf(hexChar2) == -1) + throw new IOException("Bad AVA value: bad hex value."); + b = (Character.digit(hexChar1, 16)<<4) + + Character.digit(hexChar2, 16); + berStream.write(b); + } + if (berStream.size() == 0) + throw new IOException("bad AVA format: invalid hex value"); + + value = parseAVAValue(berStream.toByteArray(), oid); + + while (c == ' ' && c != -1) + c = in.read(); + } + else + { + valueBuf = new StringBuffer (); + boolean quoted = false; + if (c == '"') { + quoted = true; + endChars = quotedEndChars; + if ((c = in.read()) == -1) + throw new IOException("Bad AVA format: Missing attrValue"); + } else { + endChars = valueEndChars; + } + + // QUOTATION * ( quotechar / pair ) QUOTATION + // quotechar = any character except '\' or QUOTATION + // pair = '\' ( special | '\' | QUOTATION | hexpair ) + while (c != -1 && endChars.indexOf(c) == -1) { + if (c == '\\') { + if ((c = in.read()) == -1) + throw new IOException("Bad AVA format: expecting " + + "escaped char."); + // expect escaping of special chars, space and CR. + if (specialChars.indexOf((char)c) != -1 || c == '\n' || + c == '\\' || c == '"' || c == ' ') { + valueBuf.append((char)c); + } + else if (hexDigits.indexOf(c) != -1) { + hexCharsBuf = new CharArrayWriter(); + // handle sequence of '\' hexpair + do { + hexChar1 = (char)c; + hexChar2 = (char)(c = in.read()); + if (hexDigits.indexOf((char)c) == -1) + throw new IOException("Bad AVA format: " + + "invalid escaped hex pair"); + hexCharsBuf.write(hexChar1); + hexCharsBuf.write(hexChar2); + // read ahead to next '\' hex-char if any. + if ((c = in.read()) == -1) + break; + if (c != '\\') { + in.unread(c); + break; + } + if ((c = in.read()) == -1) + throw new IOException("Bad AVA format: "+ + "expecting escaped char."); + if (hexDigits.indexOf((char)c) == -1) { + in.unread(c); + in.unread((int)'\\'); + break; + } + } while (true); + valueBuf.append( + getStringFromHexpairs(hexCharsBuf.toCharArray())); + } + else { + throw new IOException("Bad AVA format: " + + "invalid escaping"); + } + } + else + valueBuf.append((char)c); + c = in.read(); + } + + value = parseAVAValue( + valueBuf.toString().trim(), oid, encodingOrder); + + if (quoted) { // move to next non-white space + do { + c = in.read(); + } while (c == ' '); + if (c != -1 && valueEndChars.indexOf(c) == -1) + throw new IOException( + "Bad AVA format: separator expected at end of ava."); + } + } + + if (c != -1) + in.unread(c); + + return new AVA(oid, value); + } + + /** + * Converts a AVA keyword from a Ldap DN string to an ObjectIdentifier + * from the attribute map or, if this keyword is an OID not + * in the attribute map, create a new ObjectIdentifier for the keyword + * if acceptUnknownOids is true. + * + * @param avaKeyword AVA keyword from a Ldap DN string. + * + * @return a ObjectIdentifier object + * @exception IOException if the keyword is an OID not in the attribute + * map and acceptUnknownOids is false, or + * if an error occurs during conversion. + */ + public ObjectIdentifier parseAVAKeyword(String avaKeyword) + throws IOException + { + String keyword = avaKeyword.toUpperCase().trim(); + String oid_str = null; + ObjectIdentifier oid, new_oid; + + + if (Character.digit(keyword.charAt(0), 10) != -1) { + // value is an oid string of 1.2.3.4 + oid_str = keyword; + } + else if (keyword.startsWith("oid.") || keyword.startsWith("OID.")) { + // value is an oid string of oid.1.2.3.4 or OID.1.2... + oid_str = keyword.substring(4); + } + + if (oid_str != null) { + // value is an oid string of 1.2.3.4 or oid.1.2.3.4 or OID.1.2... + new_oid = new ObjectIdentifier(oid_str); + oid = attrMap.getOid(new_oid); + if (oid == null) { + if (!acceptUnknownOids) + throw new IOException("Unknown AVA OID."); + oid = new_oid; + } + } + else { + oid = attrMap.getOid(keyword); + if (oid == null) + throw new IOException("Unknown AVA keyword '"+keyword+"'."); + } + + return oid; + } + + /** + * Converts a AVA value from a Ldap dn string to a + * DerValue according the attribute type. For example, a value for + * CN, OU or O is expected to be a Directory String and will be converted + * to a DerValue of ASN.1 type PrintableString, T61String or + * UniversalString. A Directory String is a ASN.1 CHOICE of Printable, + * T.61 or Universal string. + * + * @param avaValueString a attribute value from a Ldap DN string. + * @param oid OID of the attribute. + * + * @return DerValue for the value. + * + * @exception IOException if an error occurs during conversion. + * @see AVAValueConverter + */ + public DerValue parseAVAValue(String avaValueString, ObjectIdentifier oid) + throws IOException + { + return parseAVAValue(avaValueString, oid, null); + } + + /** + * Like parseAVAValue(String) with a DER encoding order given as argument + * for Directory Strings. + */ + public DerValue parseAVAValue( + String avaValueString, ObjectIdentifier oid, byte[] encodingOrder) + throws IOException + { + AVAValueConverter valueConverter = attrMap.getValueConverter(oid); + if (valueConverter == null) { + if (!acceptUnknownOids) { + throw new IllegalArgumentException( + "Unrecognized OID for AVA value conversion"); + } else { + valueConverter = new GenericValueConverter(); + } + } + return valueConverter.getValue(avaValueString, encodingOrder); + } + + /** + * Converts a value in BER encoding, for example given in octothorpe form + * in a Ldap v3 dn string, to a DerValue. Checks if the BER encoded value + * is a legal value for the attribute. <p> + * <strong><i>NOTE:</i></strong> only DER encoded values are supported for + * the BER encoded value. + * + * @param berValue a value in BER encoding + * @param oid ObjectIdentifier of the attribute. + * + * @return DerValue for the BER encoded value + * @exception IOException if an error occurs during conversion. + */ + public DerValue parseAVAValue(byte[] berValue, ObjectIdentifier oid) + throws IOException + { + AVAValueConverter valueConverter = attrMap.getValueConverter(oid); + if (valueConverter == null && !acceptUnknownOids) { + throw new IllegalArgumentException( + "Unrecognized OID for AVA value conversion"); + } else { + valueConverter = new GenericValueConverter(); + } + return valueConverter.getValue(berValue); + } + + + // + // public encoding methods. + // + + /** + * Converts a X500Name object to a Ldap v3 DN string (except in unicode). + * + * @param x500name a X500Name + * + * @return a Ldap v3 DN String (except in unicode). + * + * @exception IOException if an error is encountered during conversion. + */ + public String encodeDN(X500Name x500name) + throws IOException + { + RDN[] rdns = x500name.getNames(); + // String fullname = null; + StringBuffer fullname = new StringBuffer(); + String s; + int i; + if (rdns.length == 0) + return ""; + i = rdns.length-1; + fullname.append(encodeRDN(rdns[i--])); + while (i >= 0) { + s = encodeRDN(rdns[i--]); + fullname.append(","); + fullname.append(s); + }; + return fullname.toString(); + } + + /** + * Converts a RDN to a Ldap v3 DN string (except in unicode). + * + * @param rdn a RDN + * + * @return a LDAP v3 DN string (except in unicode). + * + * @exception IOException if an error is encountered during conversion. + */ + public String encodeRDN(RDN rdn) + throws IOException + { + AVA[] avas = rdn.getAssertion(); + // String relname = null; + StringBuffer relname = new StringBuffer(); + String s; + int i=0; + + relname.append(encodeAVA(avas[i++])); + while (i <avas.length) { + s = encodeAVA(avas[i++]); + relname.append("+"); + relname.append(s); + }; + return relname.toString(); + } + + /** + * Converts a AVA to a Ldap v3 DN String (except in unicode). + * + * @param ava an AVA + * + * @return a Ldap v3 DN string (except in unicode). + * + * @exception IOException If an error is encountered during exception. + */ + public String encodeAVA(AVA ava) + throws IOException + { + if(ava == null) + { + return ""; + } + ObjectIdentifier oid = ava.getOid(); + DerValue value = ava.getValue(); + String keyword, valueStr; + + // get attribute name + + keyword = encodeOID(oid); + valueStr = encodeValue(value, oid); + + return keyword+"="+valueStr; + } + + /** + * Converts an OID to a attribute keyword in a Ldap v3 DN string + * - either a keyword if known or a string of "1.2.3.4" syntax. + * + * @param oid a ObjectIdentifier + * + * @return a keyword to use in a Ldap V3 DN string. + * + * @exception IOException if an error is encountered during conversion. + */ + public String encodeOID(ObjectIdentifier oid) + throws IOException + { + String keyword = attrMap.getName(oid); + if (keyword == null) { + if (acceptUnknownOids) + keyword = oid.toString(); + else + throw new IOException("Unknown OID"); + } + return keyword; + } + + /** + * Converts a value as a DerValue to a string in a Ldap V3 DN String. + * If the value cannot be converted to a string it will be encoded in + * octothorpe form. + * + * @param attrValue a value as a DerValue. + * @param oid OID for the attribute. + * @return a string for the value in a LDAP v3 DN String + * @exception IOException if an error occurs during conversion. + */ + public String encodeValue(DerValue attrValue, ObjectIdentifier oid) + throws IOException + { + /* + * Construct the value with as little copying and garbage + * production as practical. + */ + StringBuffer retval = new StringBuffer (30); + int i,j; + String temp = null; + AVAValueConverter valueConverter; + + X500NameAttrMap lAttrMap = attrMap; + + + if(attrValue.tag == DerValue.tag_UTF8String) + { + lAttrMap = X500NameAttrMap.getDirDefault(); + + } + + valueConverter = lAttrMap.getValueConverter(oid); + if (valueConverter == null) { + if (acceptUnknownOids) + valueConverter = new GenericValueConverter(); + else + throw new IOException( + "Unknown AVA type for encoding AVA value"); + } + + try { + temp = valueConverter.getAsString(attrValue); + + if (temp == null) { + // convert to octothorpe form. + byte data [] = attrValue.toByteArray(); + + retval.append ('#'); + for (i = 0; i < data.length; i++) { + retval.append (hexDigits.charAt ((data [i] >> 4) & 0x0f)); + retval.append (hexDigits.charAt (data [i] & 0x0f)); + } + + } else { + + retval.append(encodeString(temp)); + + } + } catch (IOException e) { + throw new IllegalArgumentException ("malformed AVA DER Value"); + } + + return retval.toString (); + } + + /** + * converts a raw value string to a string in Ldap V3 DN string format. + * @param valueStr a 'raw' value string. + * @return a attribute value string in Ldap V3 DN string format. + */ + public String encodeString(String valueStr) + { + int i,j; + int len; + StringBuffer retval = new StringBuffer(); + + /* + * generate string according to ldapv3 DN. escaping is used. + * Strings generated this way are acceptable by rfc1779 + * implementations. + */ + len = valueStr.length (); + + // get index of first space at the end of the string. + for (j = len-1; j >= 0 && valueStr.charAt(j) == ' '; j--) + continue; + + // escape spaces at the beginning of the string. + for (i = 0; i <= j && valueStr.charAt(i) == ' '; i++) { + retval.append('\\'); + retval.append(valueStr.charAt(i)); + } + + // escape special characters in the middle of the string. + for ( ; i <= j ; i++) { + if (valueStr.charAt(i) == '\\') { + retval.append('\\'); + retval.append(valueStr.charAt(i)); + } else + if (specialChars.indexOf(valueStr.charAt(i)) != -1) { + retval.append('\\'); + retval.append(valueStr.charAt(i)); + } + else + retval.append(valueStr.charAt(i)); + } + + // esacape spaces at the end. + for ( ; i < valueStr.length(); i++) { + retval.append('\\'); + retval.append(' '); + } + + return retval.toString(); + } + + // + // public get/set methods + // + + /** + * gets the X500NameAttrMap used by the converter. + * @return X500NameAttrMap used by this converter. + */ + public X500NameAttrMap getAttrMap() + { + return attrMap; + } + + /** + * returns true if the converter accepts unregistered attributes i.e. + * OIDS not in the X500NameAttrMap. + * @return true if converter converts attributes not in the + * X500NameAttrMap. + */ + public boolean getAcceptUnknownOids() + { + return acceptUnknownOids; + } + + // + // private and protected variables + // + + protected X500NameAttrMap attrMap; + protected boolean acceptUnknownOids; + + // + // private and protected static variables & methods. + // + + protected static final String specialChars = ",+=<>#;"; + + protected static final String valueEndChars = "+,;>"; + protected static final String quotedEndChars = "\""; + protected static final String octoEndChars = " " + valueEndChars; + + /* + * Values that aren't printable strings are emitted as BER-encoded + * hex data. + */ + protected static final String hexDigits = "0123456789ABCDEFabcdef"; + + + /** + * Parse a sequence of hex pairs, each pair a UTF8 byte to a java string. + * For example, "4C75C48D" is "Luc", the last c with caron. + */ + protected static char[] getStringFromHexpairs(char[] hexPairs) + throws UnsupportedEncodingException + { + ByteToCharConverter utf8_bcc; + byte utf8_buf[]; + char char_buf[]; + int ret; + int i,j; + + try { + utf8_bcc = ByteToCharConverter.getConverter("UTF8"); + } + catch (UnsupportedEncodingException e) { + throw new UnsupportedEncodingException( + "No UTF8 byte to char converter to use for "+ + "parsing LDAP DN String"); + } + utf8_bcc.setSubstitutionMode(false); + + utf8_buf = new byte[hexPairs.length/2]; + char_buf = new char[utf8_buf.length*utf8_bcc.getMaxCharsPerByte()]; + + for ( i=0,j=0 ; i < hexPairs.length ; i++,j++ ) + { + utf8_buf[j] = (byte) + ((Character.digit( hexPairs[i++], 16 ) << 4) + + Character.digit( hexPairs[i], 16 )); + } + try { + ret = utf8_bcc.convert( utf8_buf, 0, utf8_buf.length, + char_buf, 0, char_buf.length ); + } + catch (java.io.CharConversionException e) { + throw new IllegalArgumentException( + "Invalid hex pair in LDAP DN String." ); + } + + char [] out_buf = new char[ ret ]; + System.arraycopy( char_buf, 0, out_buf, 0, ret ); + return out_buf; + } + +} diff --git a/pki/base/util/src/netscape/security/x509/NSCCommentExtension.java b/pki/base/util/src/netscape/security/x509/NSCCommentExtension.java new file mode 100644 index 00000000..ea4d2520 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/NSCCommentExtension.java @@ -0,0 +1,223 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.security.cert.CertificateException; +import java.security.cert.CertificateParsingException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.util.*; +import netscape.security.util.*; + +/** + * This class defines the NSCCommentExtension + * + *@author asondhi + *@see Extension + *@see CertAttrSet + */ +public class NSCCommentExtension extends Extension +implements CertAttrSet { + + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.extensions.CommentExtension"; + /** + * Attribute names. + */ + public static final String NAME = "NSCCommentExtension"; + public static final String INFOS = "infos"; + public static final ObjectIdentifier OID = + new ObjectIdentifier("2.16.840.1.113730.1.13"); + public String mComment = null; + + + // Private data members + private Vector mInfos; + + private PrettyPrintFormat pp = new PrettyPrintFormat(":"); + + // Encode this extension value + private void encodeThis() throws IOException { + DerOutputStream os = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + + os.putIA5String(mComment); + // os.write(DerValue.tag_Sequence,tmp); + extensionValue = os.toByteArray(); + } + + /** + * Create a NSCCommentExtension with the Vector of CertificatePolicyInfo. + * + * @param infos the Vector of CertificatePolicyInfo. + */ + public NSCCommentExtension(boolean critical, String comment) throws IOException { + this.mComment = comment; + this.extensionId = new ObjectIdentifier("2.16.840.1.113730.1.13"); + this.critical = critical; + encodeThis(); + } + + /** + * Create a default NSCCommentExtension. + */ + public NSCCommentExtension(boolean critical) { + this.extensionId = new ObjectIdentifier("2.16.840.1.113730.1.13"); + this.critical = critical; + mInfos = new Vector(1,1); + } + + /** + * Create the extension from the passed DER encoded value. + * + * @param critical true if the extension is to be treated as critical. + * @param value Array of DER encoded bytes of the actual value. + * @exception IOException on error. + */ + public NSCCommentExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = new ObjectIdentifier("2.16.840.1.113730.1.13"); + + this.critical = critical.booleanValue(); + + int len = Array.getLength(value); + byte [] extValue = new byte[len]; + for (int i = 0; i < len; i++) { + extValue[i] = Array.getByte(value, i); + } + this.extensionValue = extValue; + DerValue val = new DerValue(extValue); + + mComment = val.getIA5String(); + } + + /** + * Returns a printable representation of the policy extension. + */ + public String toString() { + if (mInfos == null) return ""; + String s = super.toString() + "Netscape Comment [\n" + + mInfos.toString() + "]\n"; + + return (s); + } + + public String toPrint(int indent) { + String s; + s = "Comment :\n" + pp.indent(indent+4) + + ((mComment == null) ? "" : mComment.trim()) + "\n"; + + return (s); + } + /** + * Write the extension to the OutputStream. + * + * @param out the OutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (extensionValue == null) { + extensionId = new ObjectIdentifier("2.16.840.1.113730.1.13"); + critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Decode the extension from the InputStream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception IOException on decoding or validity errors. + */ + public void decode(InputStream in) throws IOException { + throw new IOException("Method not to be called directly."); + } + + public String getComment() { + return mComment; + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + clearValue(); + if (name.equalsIgnoreCase(INFOS)) { + if (!(obj instanceof Vector)) { + throw new IOException("Attribute value should be of" + + " type Vector."); + } + mInfos = (Vector)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:NSCCommentExtension."); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(INFOS)) { + return (mInfos); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:NSCCommentExtension."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(INFOS)) { + mInfos = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:NSCCommentExtension."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements () { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(mInfos); + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName () { + return (NAME); + } + +} diff --git a/pki/base/util/src/netscape/security/x509/NameConstraintsExtension.java b/pki/base/util/src/netscape/security/x509/NameConstraintsExtension.java new file mode 100644 index 00000000..704ebe5e --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/NameConstraintsExtension.java @@ -0,0 +1,313 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.security.cert.CertificateException; +import java.util.Enumeration; + +import netscape.security.util.*; + + +/** + * This class defines the Name Constraints Extension. + * <p> + * The name constraints extension provides permitted and excluded + * subtrees that place restrictions on names that may be included within + * a certificate issued by a given CA. Restrictions may apply to the + * subject distinguished name or subject alternative names. Any name + * matching a restriction in the excluded subtrees field is invalid + * regardless of information appearing in the permitted subtrees. + * <p> + * The ASN.1 syntax for this is: + * <pre> + * NameConstraints ::= SEQUENCE { + * permittedSubtrees [0] GeneralSubtrees OPTIONAL, + * excludedSubtrees [1] GeneralSubtrees OPTIONAL + * } + * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree + * GeneralSubtree ::== SEQUENCE { + * base GeneralName, + * minimum [0] BaseDistance DEFAULT 0, + * maximum [1] BaseDistance OPTIONAL } + * BaseDistance ::== INTEGER (0..MAX) + * </pre> + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.10 + * @see Extension + * @see CertAttrSet + */ +public class NameConstraintsExtension extends Extension +implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.extensions.NameConstraints"; + /** + * Attribute names. + */ + public static final String NAME = "NameConstraints"; + public static final String PERMITTED_SUBTREES = "permitted_subtrees"; + public static final String EXCLUDED_SUBTREES = "excluded_subtrees"; + + // Private data members + private static final byte TAG_PERMITTED = 0; + private static final byte TAG_EXCLUDED = 1; + + private GeneralSubtrees permitted; + private GeneralSubtrees excluded; + + private PrettyPrintFormat pp = new PrettyPrintFormat(":"); + + // Encode this extension value. + private void encodeThis() throws IOException { + DerOutputStream seq = new DerOutputStream(); + + DerOutputStream tagged = new DerOutputStream(); + if (permitted != null) { + DerOutputStream tmp = new DerOutputStream(); + permitted.encode(tmp); + tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + true, TAG_PERMITTED), tmp); + } + if (excluded != null) { + DerOutputStream tmp = new DerOutputStream(); + excluded.encode(tmp); + tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + true, TAG_EXCLUDED), tmp); + } + if (permitted == null && excluded == null) { + extensionValue = null; // no need to encode this extension + } else { + seq.write(DerValue.tag_Sequence, tagged); + this.extensionValue = seq.toByteArray(); + } + } + + /** + * The default constructor for this class. Either parameter + * can be set to null to indicate it is omitted but both + * cannot be null. + * + * @param permitted the permitted GeneralSubtrees (null for optional). + * @param excluded the excluded GeneralSubtrees (null for optional). + */ + public NameConstraintsExtension(GeneralSubtrees permitted, + GeneralSubtrees excluded) + throws IOException { + init(false, permitted, excluded); + } + + public NameConstraintsExtension(boolean critical, + GeneralSubtrees permitted, GeneralSubtrees excluded) + throws IOException { + init(critical, permitted, excluded); + } + + private void init(boolean critical, + GeneralSubtrees permitted, GeneralSubtrees excluded) + throws IOException { + if (permitted == null && excluded == null) { + throw new IOException("NameConstraints: Invalid arguments"); + } + this.permitted = permitted; + this.excluded = excluded; + + this.extensionId = PKIXExtensions.NameConstraints_Id; + this.critical = critical; + encodeThis(); + } + + /** + * Create the extension from the passed DER encoded value. + * + * @param critical true if the extension is to be treated as critical. + * @param value Array of DER encoded bytes of the actual value. + * @exception IOException on error. + */ + public NameConstraintsExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.NameConstraints_Id; + this.critical = critical.booleanValue(); + + if (!(value instanceof byte[])) + throw new IOException("Illegal argument type"); + + int len = Array.getLength(value); + byte[] extValue = new byte[len]; + System.arraycopy(value, 0, extValue, 0, len); + + this.extensionValue = extValue; + DerValue val = new DerValue(extValue); + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for" + + " NameConstraintsExtension."); + } + + // NB. this is always encoded with the IMPLICIT tag + // The checks only make sense if we assume implicit tagging, + // with explicit tagging the form is always constructed. + while (val.data.available() != 0) { + DerValue opt = val.data.getDerValue(); + + if (opt.isContextSpecific(TAG_PERMITTED) && opt.isConstructed()) { + if (permitted != null) { + throw new IOException("Duplicate permitted " + + "GeneralSubtrees in NameConstraintsExtension."); + } + opt.resetTag(DerValue.tag_Sequence); + permitted = new GeneralSubtrees(opt); + + } else if (opt.isContextSpecific(TAG_EXCLUDED) && + opt.isConstructed()) { + if (excluded != null) { + throw new IOException("Duplicate excluded " + + "GeneralSubtrees in NameConstraintsExtension."); + } + opt.resetTag(DerValue.tag_Sequence); + excluded = new GeneralSubtrees(opt); + } else + throw new IOException("Invalid encoding of " + + "NameConstraintsExtension."); + } + } + + /** + * Return the printable string. + */ + public String toString() { + return (super.toString() + "NameConstraints: [" + + ((permitted == null) ? "" : + ("\n Permitted:" + permitted.toString())) + + ((excluded == null) ? "" : + ("\n Excluded:" + excluded.toString())) + + " ]\n"); + } + + public String toPrint(int indent) { + return ("GeneralSubtrees: "+ + ((permitted == null) ? "" : + ("\n"+pp.indent(indent+2)+"Permitted:" + permitted.toPrint(indent+4))) + + ((excluded == null) ? "" : + ("\n"+pp.indent(indent+2)+"Excluded:" + excluded.toPrint(indent+4))) + "\n"); + + } + + /** + * Decode the extension from the InputStream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception IOException on decoding or validity errors. + */ + public void decode(InputStream in) throws IOException { + throw new IOException("Method not to be called directly."); + } + + /** + * Write the extension to the OutputStream. + * + * @param out the OutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (this.extensionValue == null) { + this.extensionId = PKIXExtensions.NameConstraints_Id; + this.critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + clearValue(); + if (name.equalsIgnoreCase(PERMITTED_SUBTREES)) { + if (!(obj instanceof GeneralSubtrees)) { + throw new IOException("Attribute value should be" + + " of type GeneralSubtrees."); + } + permitted = (GeneralSubtrees)obj; + } else if (name.equalsIgnoreCase(EXCLUDED_SUBTREES)) { + if (!(obj instanceof GeneralSubtrees)) { + throw new IOException("Attribute value should be " + + "of type GeneralSubtrees."); + } + excluded = (GeneralSubtrees)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:NameConstraintsExtension."); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(PERMITTED_SUBTREES)) { + return (permitted); + } else if (name.equalsIgnoreCase(EXCLUDED_SUBTREES)) { + return (excluded); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:NameConstraintsExtension."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(PERMITTED_SUBTREES)) { + permitted = null; + } else if (name.equalsIgnoreCase(EXCLUDED_SUBTREES)) { + excluded = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:NameConstraintsExtension."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements () { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(PERMITTED_SUBTREES); + elements.addElement(EXCLUDED_SUBTREES); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName () { + return (NAME); + } +} diff --git a/pki/base/util/src/netscape/security/x509/NoticeReference.java b/pki/base/util/src/netscape/security/x509/NoticeReference.java new file mode 100644 index 00000000..8e4711e2 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/NoticeReference.java @@ -0,0 +1,93 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.util.Vector; +import java.io.IOException; +import netscape.security.util.*; + + +/** + * Represent the NoticeReference. + * + * NoticeReference ::= SEQUENCE { + * organization DisplayText, + * noticeNumbers SEQUENCE OF INTEGER + * } + * + * @author Thomas Kwan + */ +public class NoticeReference { + + private DisplayText mOrg = null; + private int mNumbers[] = null; + + public NoticeReference(DisplayText org, int numbers[]) { + mOrg = org; + mNumbers = numbers; + } + + public NoticeReference(DerValue val) throws IOException { + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for NoticeReference"); + } + mOrg = new DisplayText(val.data.getDerValue()); + DerValue integers = val.data.getDerValue(); + if (integers.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for NoticeReference (integers)"); + } + Vector num = new Vector(); + while (integers.data.available() != 0) { + DerValue i = integers.data.getDerValue(); + BigInt bigI = i.getInteger(); + num.addElement(bigI); + } + if (num.size() <= 0) + return; + mNumbers = new int[num.size()]; + for (int i = 0; i < num.size(); i++) { + mNumbers[i] = ((BigInt)num.elementAt(i)).toInt(); + } + } + + public DisplayText getOrganization() + { + return mOrg; + } + + public int[] getNumbers() { + return mNumbers; + } + + /** + * Write the NoticeReference to the DerOutputStream. + * + * @param out the DerOutputStream to write the object to. + * @exception IOException on errors. + */ + public void encode(DerOutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + mOrg.encode(tmp); + DerOutputStream iseq = new DerOutputStream(); + for (int i = 0; i < mNumbers.length; i++) { + iseq.putInteger(new BigInt(mNumbers[i])); + } + tmp.write(DerValue.tag_Sequence,iseq); + out.write(DerValue.tag_Sequence,tmp); + } +} diff --git a/pki/base/util/src/netscape/security/x509/OIDMap.java b/pki/base/util/src/netscape/security/x509/OIDMap.java new file mode 100644 index 00000000..7d9a032d --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/OIDMap.java @@ -0,0 +1,316 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateParsingException; +import java.util.Hashtable; +import java.util.Enumeration; +import java.util.Properties; + +import netscape.security.util.*; + +/** + * This class defines the mapping from OID & name to classes and vice + * versa. Used by CertificateExtensions & PKCS10 to get the java + * classes associated with a particular OID/name. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.12 + */ +public class OIDMap { + + /** + * Location for where the OID/Classes maps are stored on + * the local system. + */ + public static final String EXTENSIONS_HOME = + (System.getProperty("java.home") + File.separator + "lib" + + File.separator + "security" + File.separator + "cert" + + File.separator); + /** + * File names for where OIDs and Classes are registered + * for V3 extensions. + */ + public static final String EXTENSIONS_OIDS = "x509extensions.oid"; + public static final String EXTENSIONS_CLASSES = "x509extensions.classes"; + + // Make default names easier + private static final String ROOT = X509CertImpl.NAME + "." + + X509CertInfo.NAME + "." + + X509CertInfo.EXTENSIONS; + private static final String AUTH_KEY_IDENTIFIER = ROOT + "." + + AuthorityKeyIdentifierExtension.NAME; + private static final String SUB_KEY_IDENTIFIER = ROOT + "." + + SubjectKeyIdentifierExtension.NAME; + private static final String KEY_USAGE = ROOT + "." + + KeyUsageExtension.NAME; + private static final String PRIVATE_KEY_USAGE = ROOT + "." + + PrivateKeyUsageExtension.NAME; + private static final String POLICY_MAPPINGS = ROOT + "." + + PolicyMappingsExtension.NAME; + private static final String SUB_ALT_NAME = ROOT + "." + + SubjectAlternativeNameExtension.NAME; + private static final String ISSUER_ALT_NAME = ROOT + "." + + IssuerAlternativeNameExtension.NAME; + private static final String BASIC_CONSTRAINTS = ROOT + "." + + BasicConstraintsExtension.NAME; + private static final String NAME_CONSTRAINTS = ROOT + "." + + NameConstraintsExtension.NAME; + private static final String POLICY_CONSTRAINTS = ROOT + "." + + PolicyConstraintsExtension.NAME; + private static final String CERT_POLICIES = //ROOT + "." + + CertificatePoliciesExtension.NAME; + private static final String SUBJ_DIR_ATTR = //ROOT + "." + + SubjectDirAttributesExtension.NAME; + public static final String EXT_KEY_USAGE_NAME = "ExtendedKeyUsageExtension"; + public static final String EXT_INHIBIT_ANY_POLICY_NAME = "InhibitAnyPolicyExtension"; + private static final String EXT_KEY_USAGE = //ROOT + "." + + EXT_KEY_USAGE_NAME; + + private static final String CRL_NUMBER = ROOT + "." + + CRLNumberExtension.NAME; + private static final String CRL_REASON = ROOT + "." + + CRLReasonExtension.NAME; + + private static final Hashtable oid2Name = new Hashtable(); + private static final Hashtable name2OID = new Hashtable(); + private static final Hashtable name2Class = new Hashtable(); + + // Initialize recognized extensions from EXTENSIONS_{OIDS/CLASSES} files + static { + loadNames(); + loadClasses(); + } + + // Load the default name to oid map (EXTENSIONS_OIDS) + private static void loadNamesDefault(Properties props) { + props.put(SUB_KEY_IDENTIFIER,"2.5.29.14"); + props.put(KEY_USAGE,"2.5.29.15"); + props.put(PRIVATE_KEY_USAGE,"2.5.29.16"); + props.put(SUB_ALT_NAME,"2.5.29.17"); + props.put(ISSUER_ALT_NAME,"2.5.29.18"); + props.put(BASIC_CONSTRAINTS,"2.5.29.19"); + props.put(CRL_NUMBER,"2.5.29.20"); + props.put(CRL_REASON,"2.5.29.21"); + props.put(NAME_CONSTRAINTS,"2.5.29.30"); + props.put(POLICY_MAPPINGS,"2.5.29.33"); + props.put(POLICY_CONSTRAINTS,"2.5.29.36"); + props.put(CERT_POLICIES,"2.5.29.32"); + props.put(AUTH_KEY_IDENTIFIER,"2.5.29.35"); + props.put(SUBJ_DIR_ATTR,"2.5.29.9"); + props.put(EXT_KEY_USAGE,"2.5.29.37"); + } + + // Load the default name to class map (EXTENSIONS_CLASSES) + private static void loadClassDefault (Properties props) { + props.put(AUTH_KEY_IDENTIFIER, + "netscape.security.x509.AuthorityKeyIdentifierExtension"); + props.put(SUB_KEY_IDENTIFIER, + "netscape.security.x509.SubjectKeyIdentifierExtension"); + props.put(KEY_USAGE, + "netscape.security.x509.KeyUsageExtension"); + props.put(PRIVATE_KEY_USAGE, + "netscape.security.x509.PrivateKeyUsageExtension"); + props.put(POLICY_MAPPINGS, + "netscape.security.x509.PolicyMappingsExtension"); + props.put(SUB_ALT_NAME, + "netscape.security.x509.SubjectAlternativeNameExtension"); + props.put(ISSUER_ALT_NAME, + "netscape.security.x509.IssuerAlternativeNameExtension"); + props.put(BASIC_CONSTRAINTS, + "netscape.security.x509.BasicConstraintsExtension"); + props.put(NAME_CONSTRAINTS, + "netscape.security.x509.NameConstraintsExtension"); + props.put(POLICY_CONSTRAINTS, + "netscape.security.x509.PolicyConstraintsExtension"); + props.put(CERT_POLICIES, + "netscape.security.x509.CertificatePoliciesExtension"); + props.put(SUBJ_DIR_ATTR, + "netscape.security.x509.SubjectDirAttributesExtension"); + props.put(EXT_KEY_USAGE, + "netscape.security.extensions.ExtendedKeyUsageExtension"); + props.put(CRL_NUMBER, "netscape.security.x509.CRLNumberExtension"); + props.put(CRL_REASON, "netscape.security.x509.CRLReasonExtension"); + } + + // Return the file along with location + private static File certificatePropFile (String fileName) { + return (new File(EXTENSIONS_HOME + fileName)); + } + + // Load the names to oid map + private static void loadNames() { + Properties props = new Properties(); + File namesMap = certificatePropFile(EXTENSIONS_OIDS); + + if (!namesMap.exists()) { + loadNamesDefault(props); + } else { + try { + FileInputStream fis = new FileInputStream(namesMap); + props.load(fis); + fis.close(); + } catch (IOException e) { + loadNamesDefault(props); + } + } + + Enumeration names = props.propertyNames(); + while (names.hasMoreElements()) { + String name = (String)names.nextElement(); + String oidName = props.getProperty(name); + ObjectIdentifier oid = new ObjectIdentifier(oidName); + + name2OID.put(name, oid); + oid2Name.put(oid, name); + } + } + + // Load the names to classes map + private static void loadClasses() { + Properties props = new Properties(); + File classMap = certificatePropFile(EXTENSIONS_CLASSES); + + if (!classMap.exists()) { + loadClassDefault(props); + } else { + try { + FileInputStream fis = new FileInputStream(classMap); + props.load(fis); + } catch (IOException e) { + loadClassDefault(props); + } + } + + Enumeration names = props.propertyNames(); + while (names.hasMoreElements()) { + String name = (String)names.nextElement(); + String className = props.getProperty(name); + + name2Class.put(name, className); + } + } + + // save to file + private static void saveDefaults(Properties props, String fileName) { + try { + FileOutputStream fos = new FileOutputStream(fileName); + props.save(fos, "Registered classes"); + fos.close(); + } catch (IOException e) { + System.err.println(e); + } + } + + /** + * Add a name to lookup table. + * + * @param className the name of the fully qualified class implementing + * the asn object. + * @param oid the string representation of the object identifier for + * the class. + * @param name the name of the attribute. + * @exception CertificateException on errors. + */ + public static void addAttribute(String className, String oid, String name) + throws CertificateException { + ObjectIdentifier objId = new ObjectIdentifier(oid); + if (oid2Name.get(objId) != null) { + throw new CertificateException("Object identifier already exists."); + } + if (name2OID.get(name) != null) { + throw new CertificateException("Name already exists."); + } + if (name2Class.get(className) != null) { + throw new CertificateException("Class already exists."); + } + oid2Name.put(objId, name); + name2OID.put(name, objId); + name2Class.put(name, className); + } + + /** + * Return user friendly name associated with the OID. + * + * @param oid the name of the object identifier to be returned. + * @return the user friendly name or null if no name + * is registered for this oid. + */ + public static String getName(ObjectIdentifier oid) { + return (String)oid2Name.get(oid); + } + + /** + * Return Object identifier for user friendly name. + * + * @param name the user friendly name. + * @return the Object Identifier or null if no oid + * is registered for this name. + */ + public static ObjectIdentifier getOID(String name) { + return (ObjectIdentifier)name2OID.get(name); + } + + /** + * Return the java class object associated with the user friendly name. + * + * @param name the user friendly name. + * @exception CertificateException if class cannot be instantiated. + */ + public static Class getClass(String name) throws CertificateException { + String className = (String)name2Class.get(name); + if (className == null) + return null; + try { + Class extClass = Class.forName(className); + return (extClass); + } catch (Exception e) { + throw new CertificateException("Error instantiating class for " + + name + " " + e.toString()); + } + } + + /** + * Return the java class object associated with the object identifier.. + * + * @param oid the name of the object identifier to be returned. + * @exception CertificateException if class cannot be instatiated. + */ + public static Class getClass(ObjectIdentifier oid) + throws CertificateException { + String name = getName(oid); + if (name == null) + return null; + String className = (String)name2Class.get(name); + if (className == null) + return null; + try { + Class extClass = Class.forName(className); + return (extClass); + } catch (Exception e) { + throw new CertificateException("Error instantiating class for " + + name + " " + e.toString()); + } + } +} diff --git a/pki/base/util/src/netscape/security/x509/OIDName.java b/pki/base/util/src/netscape/security/x509/OIDName.java new file mode 100644 index 00000000..a0e12cca --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/OIDName.java @@ -0,0 +1,84 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; + +import netscape.security.util.*; + +/** + * This class implements the OIDName as required by the GeneralNames + * ASN.1 object. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.3 + * @see GeneralName + * @see GeneralNames + * @see GeneralNameInterface + */ +public class OIDName implements GeneralNameInterface { + private ObjectIdentifier oid; + + /** + * Create the OIDName object from the passed encoded Der value. + * + * @param derValue the encoded DER OIDName. + * @exception IOException on error. + */ + public OIDName(DerValue derValue) throws IOException { + oid = derValue.getOID(); + } + + /** + * Create the OIDName object with the specified name. + * + * @param name the OIDName. + */ + public OIDName(ObjectIdentifier oid) { + this.oid = oid; + } + + public OIDName(String oid) { + this.oid = new ObjectIdentifier(oid); + } + + /** + * Return the type of the GeneralName. + */ + public int getType() { + return (GeneralNameInterface.NAME_OID); + } + + /** + * Encode the OID name into the DerOutputStream. + * + * @param out the DER stream to encode the OIDName to. + * @exception IOException on encoding errors. + */ + public void encode(DerOutputStream out) throws IOException { + out.putOID(oid); + } + + /** + * Convert the name into user readable string. + */ + public String toString() { + return ("OIDName: " + oid.toString()); + } +} diff --git a/pki/base/util/src/netscape/security/x509/OtherName.java b/pki/base/util/src/netscape/security/x509/OtherName.java new file mode 100644 index 00000000..263d0009 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/OtherName.java @@ -0,0 +1,207 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.*; +import java.util.StringTokenizer; +import java.lang.Integer; + +import netscape.security.util.*; + +/** + * This class implements the OtherName as required by the GeneralNames + * ASN.1 object. + * + * OtherName ::= SEQUENCE { + * type-id OBJECT IDENTIFIER, + * value [0] EXPLICIT ANY DEFINED BY type-id + * } + * + * @see GeneralName + * @see GeneralNameInterface + * @see GeneralNames + * + * @version 1.2 + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class OtherName implements GeneralNameInterface { + private ObjectIdentifier mOID = null; + private byte[] mData = null; + + /** + * Create the IPAddressName object from the passed encoded Der value. + * + * @param derValue the encoded DER IPAddressName. + * @exception IOException on error. + */ + public OtherName(DerValue derValue) throws IOException { + decodeThis(derValue); + } + + public OtherName(ObjectIdentifier oid, byte data[]) { + mOID = oid; + DerOutputStream dos = new DerOutputStream(); + try { + dos.putDerValue(new DerValue(data)); + } catch (IOException e) { + } + mData = dos.toByteArray(); + } + + /** + * Constructs a string-based other name. + */ + public OtherName(ObjectIdentifier oid, byte tag, String value) { + mOID = oid; + DerOutputStream dos = new DerOutputStream(); + try { + if (tag == DerValue.tag_PrintableString) { + dos.putPrintableString(value); + } else if (tag == DerValue.tag_IA5String) { + dos.putIA5String(value); + } else if (tag == DerValue.tag_BMPString) { + dos.putBMPString(value); + } else if (tag == DerValue.tag_UTF8String) { + dos.putUTF8String(value); + } + } catch (IOException e) { + } + mData = dos.toByteArray(); + } + + public OtherName(ObjectIdentifier oid, String value) { + mOID = oid; + DerOutputStream dos = new DerOutputStream(); + try { + dos.putPrintableString(value); + } catch (IOException e) { + } + mData = dos.toByteArray(); + } + + /** + * Create the IPAddressName object with the specified name. + * + * @param name the IPAddressName. + */ + public OtherName(byte[] data) { + try { + decodeThis(new DerValue(data)); + } catch (IOException e) { + } + } + + public ObjectIdentifier getOID() + { + return mOID; + } + + /** + * Return the type of the GeneralName. + */ + public int getType() { + return (GeneralNameInterface.NAME_ANY); + } + + /** + * Encode the IPAddress name into the DerOutputStream. + * + * @param out the DER stream to encode the IPAddressName to. + * @exception IOException on encoding errors. + */ + public void encode(DerOutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + + //encoding the attributes + tmp.putOID(mOID); + DerOutputStream tmp1 = new DerOutputStream(); + tmp1.write(mData); + tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, + (byte)0x80), tmp1); + + out.write(DerValue.tag_SequenceOf, tmp); + } + + public void decode(InputStream in) throws IOException { + DerValue val = new DerValue(in); + decodeThis(val); + } + + // Decode this extension value + private void decodeThis(DerValue derVal) throws IOException { + + // if (derVal.tag != DerValue.tag_Sequence) { + // throw new IOException("Invalid encoding for other name"); + // } + + + // Decode all the Attributes + mOID = derVal.data.getOID(); + // skip tag + DerValue tag = derVal.data.getDerValue(); + // read data + DerValue data = tag.data.getDerValue(); + mData = data.toByteArray(); + } + + public byte[] getValue() { + return mData; + } + + /** + * Return a printable string of IPaddress + */ + public String toString() { + if (mData != null) { + try { + DerValue data = new DerValue(mData); + if (data.tag == DerValue.tag_PrintableString) { + return "OtherName: (PrintableString)" + mOID + "," + data.getPrintableString(); + } else if (data.tag == DerValue.tag_IA5String) { + return "OtherName: (IA5String)" + mOID + "," + data.getIA5String(); + } else if (data.tag == DerValue.tag_BMPString) { + return "OtherName: (BMPString)" + mOID + "," + data.getIA5String(); + } else if (data.tag == DerValue.tag_UTF8String) { + return "OtherName: (UTF8String)" + mOID + "," + data.getUTF8String(); + } else { + return "OtherName: (Any)" + mOID + "," + toStr(data.toByteArray()); + } + } catch (IOException e) { + + return "OtherName: (Any)" + mOID + "," + toStr(mData); + } + } else { + return "OtherName: "; + } + } + + public String toStr(byte data[]) { + StringBuffer b = new StringBuffer(); + for (int i = 0; i < data.length; i++) { + if ((data[i] & 0xff) < 16) { + b.append("0"); + } + b.append(Integer.toString((int)(data[i] & 0xff), 0x10)); + } + return b.toString(); + } +} + + diff --git a/pki/base/util/src/netscape/security/x509/PKIXExtensions.java b/pki/base/util/src/netscape/security/x509/PKIXExtensions.java new file mode 100644 index 00000000..8c6742c5 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/PKIXExtensions.java @@ -0,0 +1,204 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.*; + +import netscape.security.util.*; + +/** + * Lists all the object identifiers of the X509 extensions of the PKIX profile. + * + * <p>Extensions are addiitonal attributes which can be inserted in a X509 + * v3 certificate. For example a "Driving License Certificate" could have + * the driving license number as a extension. + * + * <p>Extensions are represented as a sequence of the extension identifier + * (Object Identifier), a boolean flag stating whether the extension is to + * be treated as being critical and the extension value itself (this is again + * a DER encoding of the extension value). + * + * @see Extension + * + * @version 1.4 + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class PKIXExtensions { + // The object identifiers + private static final int AuthorityKey_data [] = { 2, 5, 29, 35 }; + private static final int SubjectKey_data [] = { 2, 5, 29, 14 }; + private static final int KeyUsage_data [] = { 2, 5, 29, 15 }; + private static final int PrivateKeyUsage_data [] = { 2, 5, 29, 16 }; + private static final int CertificatePolicies_data [] = { 2, 5, 29, 32 }; + private static final int PolicyMappings_data [] = { 2, 5, 29, 33 }; + private static final int SubjectAlternativeName_data [] = { 2, 5, 29, 17 }; + private static final int IssuerAlternativeName_data [] = { 2, 5, 29, 18 }; + private static final int SubjectDirectoryAttributes_data [] = { 2, 5, 29, 9 }; + private static final int BasicConstraints_data [] = { 2, 5, 29, 19 }; + private static final int NameConstraints_data [] = { 2, 5, 29, 30 }; + private static final int PolicyConstraints_data [] = { 2, 5, 29, 36 }; + private static final int CRLDistributionPoints_data [] = { 2, 5, 29, 31 }; + private static final int CRLNumber_data [] = { 2, 5, 29, 20 }; + private static final int IssuingDistributionPoint_data [] = { 2, 5, 29, 28 }; + private static final int DeltaCRLIndicator_data [] = { 2, 5, 29, 27 }; + private static final int ReasonCode_data [] = { 2, 5, 29, 21 }; + private static final int HoldInstructionCode_data [] = { 2, 5, 29, 23 }; + private static final int InvalidityDate_data [] = { 2, 5, 29, 24 }; + private static final int CertificateIssuer_data [] = { 2, 5, 29, 29 }; + private static final int FreshestCRL_data [] = { 2, 5, 29, 46 }; + + /** + * Identifies the particular public key used to sign the certificate. + */ + public static final ObjectIdentifier AuthorityKey_Id + = new ObjectIdentifier(AuthorityKey_data); + + /** + * Identifies the particular public key used in an application. + */ + public static final ObjectIdentifier SubjectKey_Id + = new ObjectIdentifier(SubjectKey_data); + + /** + * Defines the purpose of the key contained in the certificate. + */ + public static final ObjectIdentifier KeyUsage_Id + = new ObjectIdentifier(KeyUsage_data); + + /** + * Allows the certificate issuer to specify a different validity period + * for the private key than the certificate. + */ + public static final ObjectIdentifier PrivateKeyUsage_Id + = new ObjectIdentifier(PrivateKeyUsage_data); + + /** + * Contains the sequence of policy information terms. + */ + public static final ObjectIdentifier CertificatePolicies_Id + = new ObjectIdentifier(CertificatePolicies_data); + + /** + * Lists pairs of objectidentifiers of policies considered equivalent by the + * issuing CA to the subject CA. + */ + public static final ObjectIdentifier PolicyMappings_Id + = new ObjectIdentifier(PolicyMappings_data); + + /** + * Allows additional identities to be bound to the subject of the certificate. + */ + public static final ObjectIdentifier SubjectAlternativeName_Id + = new ObjectIdentifier(SubjectAlternativeName_data); + + /** + * Allows additional identities to be associated with the certificate issuer. + */ + public static final ObjectIdentifier IssuerAlternativeName_Id = + new ObjectIdentifier(IssuerAlternativeName_data); + + /** + * Identifies additional directory attributes. + * This extension is always non-critical. + */ + public static final ObjectIdentifier SubjectDirectoryAttributes_Id + = new ObjectIdentifier(SubjectDirectoryAttributes_data); + + /** + * Identifies whether the subject of the certificate is a CA and how deep + * a certification path may exist through that CA. + */ + public static final ObjectIdentifier BasicConstraints_Id = + new ObjectIdentifier(BasicConstraints_data); + + /** + * Provides for permitted and excluded subtrees that place restrictions + * on names that may be included within a certificate issued by a given CA. + */ + public static final ObjectIdentifier NameConstraints_Id + = new ObjectIdentifier(NameConstraints_data); + + /** + * Used to either prohibit policy mapping or limit the set of policies + * that can be in subsequent certificates. + */ + public static final ObjectIdentifier PolicyConstraints_Id + = new ObjectIdentifier(PolicyConstraints_data); + + /** + * Identifies how CRL information is obtained. + */ + public static final ObjectIdentifier CRLDistributionPoints_Id + = new ObjectIdentifier(CRLDistributionPoints_data); + + /** + * Conveys a monotonically increasing sequence number for each CRL + * issued by a given CA. + */ + public static final ObjectIdentifier CRLNumber_Id + = new ObjectIdentifier(CRLNumber_data); + + /** + * Identifies the CRL distribution point for a particular CRL. + */ + public static final ObjectIdentifier IssuingDistributionPoint_Id + = new ObjectIdentifier(IssuingDistributionPoint_data); + + /** + * Identifies the delta CRL. + */ + public static final ObjectIdentifier DeltaCRLIndicator_Id + = new ObjectIdentifier(DeltaCRLIndicator_data); + + /** + * Identifies the reason for the certificate revocation. + */ + public static final ObjectIdentifier ReasonCode_Id + = new ObjectIdentifier(ReasonCode_data); + + /** + * This extension provides a registered instruction identifier indicating + * the action to be taken, after encountering a certificate that has been + * placed on hold. + */ + public static final ObjectIdentifier HoldInstructionCode_Id + = new ObjectIdentifier(HoldInstructionCode_data); + + /** + * Identifies the date on which it is known or suspected that the private + * key was compromised or that the certificate otherwise became invalid. + */ + public static final ObjectIdentifier InvalidityDate_Id + = new ObjectIdentifier(InvalidityDate_data); + + /** + * Identifies the date on which it is known or suspected that the private + * key was compromised or that the certificate otherwise became invalid. + */ + public static final ObjectIdentifier CertificateIssuer_Id + = new ObjectIdentifier(CertificateIssuer_data); + + /** + * Identifies how delta CRL information is obtained. + */ + public static final ObjectIdentifier FreshestCRL_Id + = new ObjectIdentifier(FreshestCRL_data); + +} diff --git a/pki/base/util/src/netscape/security/x509/PolicyConstraint.java b/pki/base/util/src/netscape/security/x509/PolicyConstraint.java new file mode 100644 index 00000000..9a0b4878 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/PolicyConstraint.java @@ -0,0 +1,134 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.security.cert.CertificateException; + +import netscape.security.util.*; + +/** + * This class defines the PolicyConstraint ASN.1 object. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.4 + */ +public class PolicyConstraint { + private static final byte TAG_SET = 0; + private static final byte TAG_REQUIRE = 1; + private static final byte TAG_INHIBIT = 2; + + private CertificatePolicySet set = null; + private int require = -1; + private int inhibit = -1; + + /** + * The default constructor for this object + * + * @param set the CertificatePolicySet (null for optional). + * @param require require explicit policy (-1 for optional). + * @param inhibit inhibit policy mapping (-1 for optional). + */ + public PolicyConstraint(CertificatePolicySet set, int require, int inhibit) { + this.set = set; + this.require = require; + this.inhibit = inhibit; + } + + /** + * Create the PolicyConstraint from the DerValue. + * + * @param val the DerValue of the PolicyConstraint. + * @exception IOException on decoding errors. + */ + public PolicyConstraint(DerValue val) throws IOException { + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Sequence tag missing for PolicyConstraint."); + } + DerInputStream in = val.data; + while (in != null && in.available() != 0) { + DerValue next = in.getDerValue(); + switch (next.tag & 0x1f) { + case TAG_SET: + this.set = new CertificatePolicySet(next.data); + break; + + case TAG_REQUIRE: + next = next.data.getDerValue(); + this.require = (next.getInteger()).toInt(); + break; + + case TAG_INHIBIT: + next = next.data.getDerValue(); + this.inhibit = (next.getInteger()).toInt(); + break; + + default: + throw new IOException("Invalid tag option for PolicyConstraint."); + } + } + } + + /** + * Return user readable form of the object. + */ + public String toString() { + String s = ((set != null) ? + "PolicyConstraint: [\n" + + " PolicySet:[" + set.toString() + "]\n" + + " Require:" + require + "\n" + + " Inhibit:" + inhibit + "\n" + + "]\n" : + "PolicyConstraint: [\n" + + " PolicySet:[null]\n" + + " Require:" + require + "\n" + + " Inhibit:" + inhibit + "\n" + + "]\n"); + return (s); + } + + /** + * Encode the object to the output stream. + * + * @param out the DerOutputStream to encode the object to. + */ + public void encode(DerOutputStream out) throws IOException { + DerOutputStream tagged = new DerOutputStream(); + + if (set != null) { + DerOutputStream tmp = new DerOutputStream(); + set.encode(tmp); + tagged.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, TAG_SET), tmp); + } + if (require != -1) { + DerOutputStream tmp = new DerOutputStream(); + tmp.putInteger(new BigInt(require)); + tagged.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, TAG_REQUIRE), tmp); + } + if (inhibit != -1) { + DerOutputStream tmp = new DerOutputStream(); + tmp.putInteger(new BigInt(inhibit)); + tagged.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, TAG_INHIBIT), tmp); + } + out.write(DerValue.tag_Sequence,tagged); + } +} diff --git a/pki/base/util/src/netscape/security/x509/PolicyConstraintsExtension.java b/pki/base/util/src/netscape/security/x509/PolicyConstraintsExtension.java new file mode 100644 index 00000000..006c1dcb --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/PolicyConstraintsExtension.java @@ -0,0 +1,301 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.security.cert.CertificateException; +import java.util.Enumeration; +import java.util.Vector; + +import netscape.security.util.*; + +/** + * This class defines the certificate extension which specifies the + * Policy constraints. + * <p> + * The policy constraints extension can be used in certificates issued + * to CAs. The policy constraints extension constrains path validation + * in two ways. It can be used to prohibit policy mapping or require + * that each certificate in a path contain an acceptable policy + * identifier.<p> + * The ASN.1 syntax for this is (IMPLICIT tagging is defined in the + * module definition): + * <pre> + * PolicyConstraints ::= SEQUENCE { + * requireExplicitPolicy [0] SkipCerts OPTIONAL, + * inhibitPolicyMapping [1] SkipCerts OPTIONAL + * } + * SkipCerts ::= INTEGER (0..MAX) + * </pre> + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.9 + * @see Extension + * @see CertAttrSet + */ +public class PolicyConstraintsExtension extends Extension +implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.extensions.PolicyConstraints"; + /** + * Attribute names. + */ + public static final String NAME = "PolicyConstraints"; + public static final String REQUIRE = "require"; + public static final String INHIBIT = "inhibit"; + + private static final byte TAG_REQUIRE = 0; + private static final byte TAG_INHIBIT = 1; + + private int require = -1; + private int inhibit = -1; + + // Encode this extension value. + private void encodeThis() throws IOException { + DerOutputStream tagged = new DerOutputStream(); + DerOutputStream seq = new DerOutputStream(); + + if (require != -1) { + DerOutputStream tmp = new DerOutputStream(); + tmp.putInteger(new BigInt(require)); + tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + false, TAG_REQUIRE), tmp); + } + if (inhibit != -1) { + DerOutputStream tmp = new DerOutputStream(); + tmp.putInteger(new BigInt(inhibit)); + tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + false, TAG_INHIBIT), tmp); + } + seq.write(DerValue.tag_Sequence, tagged); + extensionValue = seq.toByteArray(); + } + + /** + * Create a PolicyConstraintsExtension object with criticality and + * both require explicit policy and inhibit policy mapping. + * + * @param critical whether this extension should be critical + * @param require require explicit policy (-1 for optional). + * @param inhibit inhibit policy mapping (-1 for optional). + */ + public PolicyConstraintsExtension(boolean crit, int require, int inhibit) + throws IOException { + init(crit, require, inhibit); + } + + /** + * Create a PolicyConstraintsExtension object with both + * require explicit policy and inhibit policy mapping. + * + * @param require require explicit policy (-1 for optional). + * @param inhibit inhibit policy mapping (-1 for optional). + */ + public PolicyConstraintsExtension(int require, int inhibit) + throws IOException { + init(false, require, inhibit); + } + + private void init(boolean crit, int require, int inhibit) + throws IOException { + this.require = require; + this.inhibit = inhibit; + this.extensionId = PKIXExtensions.PolicyConstraints_Id; + this.critical = crit; + encodeThis(); + } + + /** + * Create the extension from its DER encoded value and criticality. + * + * @param critical true if the extension is to be treated as critical. + * @param value Array of DER encoded bytes of the actual value. + * @exception IOException on error. + */ + public PolicyConstraintsExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.PolicyConstraints_Id; + this.critical = critical.booleanValue(); + + if (!(value instanceof byte[])) + throw new IOException("Illegal argument type"); + + int len = Array.getLength(value); + byte[] extValue = new byte[len]; + System.arraycopy(value, 0, extValue, 0, len); + + this.extensionValue = extValue; + DerValue val = new DerValue(extValue); + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Sequence tag missing for PolicyConstraint."); + } + DerInputStream in = val.data; + while (in != null && in.available() != 0) { + DerValue next = in.getDerValue(); + + if (next.isContextSpecific(TAG_REQUIRE) && !next.isConstructed()) { + if (this.require != -1) + throw new IOException("Duplicate requireExplicitPolicy" + + "found in the PolicyConstraintsExtension"); + next.resetTag(DerValue.tag_Integer); + this.require = (next.getInteger()).toInt(); + + } else if (next.isContextSpecific(TAG_INHIBIT) && + !next.isConstructed()) { + if (this.inhibit != -1) + throw new IOException("Duplicate inhibitPolicyMapping" + + "found in the PolicyConstraintsExtension"); + next.resetTag(DerValue.tag_Integer); + this.inhibit = (next.getInteger()).toInt(); + } else + throw new IOException("Invalid encoding of PolicyConstraint"); + } + } + + /** + * Return the extension as user readable string. + */ + public String toString() { + String s; + s = super.toString() + "PolicyConstraints: [" + " Require: "; + if (require == -1) + s += "unspecified;"; + else + s += require + ";"; + s += "\tInhibit: "; + if (inhibit == -1) + s += "unspecified"; + else + s += inhibit; + s += " ]\n"; + return s; + } + + /** + * Decode the extension from the InputStream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception IOException on decoding or validity errors. + */ + public void decode(InputStream in) throws IOException { + throw new IOException("Method not to be called directly."); + } + + /** + * Write the extension to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (extensionValue == null) { + extensionId = PKIXExtensions.PolicyConstraints_Id; + critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + clearValue(); + if (!(obj instanceof Integer)) { + throw new IOException("Attribute value should be of type Integer."); + } + if (name.equalsIgnoreCase(REQUIRE)) { + require = ((Integer)obj).intValue(); + } else if (name.equalsIgnoreCase(INHIBIT)) { + inhibit = ((Integer)obj).intValue(); + } else { + throw new IOException("Attribute name " + "[" + name + "]" + + " not recognized by " + + "CertAttrSet:PolicyConstraints."); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(REQUIRE)) { + return Integer.valueOf(require); + } else if (name.equalsIgnoreCase(INHIBIT)) { + return Integer.valueOf(inhibit); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:PolicyConstraints."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(REQUIRE)) { + require = -1; + } else if (name.equalsIgnoreCase(INHIBIT)) { + inhibit = -1; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:PolicyConstraints."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements () { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(REQUIRE); + elements.addElement(INHIBIT); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName () { + return (NAME); + } + + /** + * returns the requireExplicitMapping parameter. + */ + public int getRequireExplicitMapping() { + return require; + } + + /** + * returns the inhibitPolicyMapping parameter. + */ + public int getInhibitPolicyMapping() { + return inhibit; + } +} diff --git a/pki/base/util/src/netscape/security/x509/PolicyMappingsExtension.java b/pki/base/util/src/netscape/security/x509/PolicyMappingsExtension.java new file mode 100644 index 00000000..f2d76968 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/PolicyMappingsExtension.java @@ -0,0 +1,252 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.security.cert.CertificateException; +import java.util.Vector; +import java.util.Enumeration; + +import netscape.security.util.*; + +/** + * Represent the Policy Mappings Extension. + * + * This extension, if present, identifies the certificate policies considered + * identical between the issuing and the subject CA. + * <p>Extensions are addiitonal attributes which can be inserted in a X509 + * v3 certificate. For example a "Driving License Certificate" could have + * the driving license number as a extension. + * + * <p>Extensions are represented as a sequence of the extension identifier + * (Object Identifier), a boolean flag stating whether the extension is to + * be treated as being critical and the extension value itself (this is again + * a DER encoding of the extension value). + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.7 + * @see Extension + * @see CertAttrSet + */ +public class PolicyMappingsExtension extends Extension +implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.extensions.PolicyMappings"; + /** + * Attribute names. + */ + public static final String NAME = "PolicyMappings"; + public static final String MAP = "map"; + + // Private data members + private Vector maps = null; + + // Encode this extension value + private void encodeThis() throws IOException { + DerOutputStream os = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + + for (int i = 0; i < maps.size(); i++) { + ((CertificatePolicyMap)maps.elementAt(i)).encode(tmp); + } + os.write(DerValue.tag_Sequence,tmp); + extensionValue = os.toByteArray(); + } + + /** + * Create a PolicyMappings with the Vector of CertificatePolicyMap. + * + * @param maps the Vector of CertificatePolicyMap. + */ + public PolicyMappingsExtension(Vector map) throws IOException { + init(false, map); + } + + /** + * Create a PolicyMappings with the Vector of CertificatePolicyMap. + * + * @param maps the Vector of CertificatePolicyMap. + */ + public PolicyMappingsExtension(boolean critical, Vector map) + throws IOException { + init(critical, map); + } + + /** + * init policy with criticality and map. + */ + private void init(boolean critical, Vector map) throws IOException { + this.maps = map; + this.extensionId = PKIXExtensions.PolicyMappings_Id; + this.critical = critical; + encodeThis(); + } + + /** + * Create a default PolicyMappingsExtension. + */ + public PolicyMappingsExtension() { + extensionId = PKIXExtensions.PolicyMappings_Id; + critical = false; + maps = new Vector(1,1); + } + + /** + * Create the extension from the passed DER encoded value. + * + * @param critical true if the extension is to be treated as critical. + * @param value Array of DER encoded bytes of the actual value. + * @exception IOException on error. + */ + public PolicyMappingsExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.PolicyMappings_Id; + this.critical = critical.booleanValue(); + + int len = Array.getLength(value); + byte [] extValue = new byte[len]; + for (int i = 0; i < len; i++) { + extValue[i] = Array.getByte(value, i); + } + this.extensionValue = extValue; + DerValue val = new DerValue(extValue); + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for " + + "PolicyMappingsExtension."); + } + maps = new Vector(1, 1); + while (val.data.available() != 0) { + DerValue seq = val.data.getDerValue(); + CertificatePolicyMap map = new CertificatePolicyMap(seq); + maps.addElement(map); + } + } + + /** + * Returns a printable representation of the policy map. + */ + public String toString() { + if (maps == null) return ""; + String s = super.toString() + "PolicyMappings [\n" + + maps.toString() + "]\n"; + + return (s); + } + + /** + * Write the extension to the OutputStream. + * + * @param out the OutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (extensionValue == null) { + extensionId = PKIXExtensions.PolicyMappings_Id; + critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Decode the extension from the InputStream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception IOException on decoding or validity errors. + */ + public void decode(InputStream in) throws IOException { + throw new IOException("Method not to be called directly."); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + clearValue(); + if (name.equalsIgnoreCase(MAP)) { + if (!(obj instanceof Vector)) { + throw new IOException("Attribute value should be of" + + " type Vector."); + } + maps = (Vector)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:PolicyMappingsExtension."); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(MAP)) { + return (maps); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:PolicyMappingsExtension."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(MAP)) { + maps = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:PolicyMappingsExtension."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements () { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(MAP); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName () { + return (NAME); + } + + /** + * Returns an enumeration of the mappings in the extension. + */ + public Enumeration getMappings() { + if (maps == null) + return null; + return maps.elements(); + } +} diff --git a/pki/base/util/src/netscape/security/x509/PolicyQualifierInfo.java b/pki/base/util/src/netscape/security/x509/PolicyQualifierInfo.java new file mode 100644 index 00000000..1c4984be --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/PolicyQualifierInfo.java @@ -0,0 +1,114 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import netscape.security.util.*; + + +/** + * Represent the PolicyQualifierInfo. + * + * policyQualifierInfo ::= SEQUENCE { + * policyQualifierId PolicyQualifierId + * qualifier ANY DEFINED BY policyQualifierId + * } + * + * @author Thomas Kwan + */ +public class PolicyQualifierInfo implements java.io.Serializable { + + public static final int OID_CPS[] = { 1, 3, 6, 1, 5, 5, 7, 2, 1 }; + public static final ObjectIdentifier QT_CPS = new + ObjectIdentifier(OID_CPS); + + public static final int OID_UNOTICE[] = { 1, 3, 6, 1, 5, 5, 7, 2, 2 }; + public static final ObjectIdentifier QT_UNOTICE = new + ObjectIdentifier(OID_UNOTICE); + + private ObjectIdentifier mId = null; + private Qualifier mQualifier = null; + + /** + * Create a PolicyQualifierInfo + * + * @param id the ObjectIdentifier for the policy id. + */ + public PolicyQualifierInfo(ObjectIdentifier id, Qualifier qualifier) { + mId = id; + mQualifier = qualifier; + } + + /** + * Create the object from its Der encoded value. + * + * @param val the DER encoded value for the same. + */ + public PolicyQualifierInfo(DerValue val) throws IOException { + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for PolicyQualifierInfo."); + } + DerValue did = val.data.getDerValue(); + mId = did.getOID(); + if (val.data.available() != 0) { + DerValue qualifier = val.data.getDerValue(); + if (qualifier.tag == DerValue.tag_IA5String) { + mQualifier = new CPSuri(qualifier); + } else { + mQualifier = new UserNotice(qualifier); + } + } + } + + public ObjectIdentifier getId() + { + return mId; + } + + /** + * Returns object of type CPSuri or UserNotice. + */ + public Qualifier getQualifier() + { + return mQualifier; + } + + /** + * Returns a printable representation of the CertificatePolicyId. + */ + public String toString() { + String s = "PolicyQualifierInfo: ["; + s = s + getId() + " " + getQualifier(); + s = s + "]\n"; + + return (s); + } + + /** + * Write the PolicyQualifier to the DerOutputStream. + * + * @param out the DerOutputStream to write the object to. + * @exception IOException on errors. + */ + public void encode(DerOutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + tmp.putOID(mId); + mQualifier.encode(tmp); + out.write(DerValue.tag_Sequence,tmp); + } +} diff --git a/pki/base/util/src/netscape/security/x509/PolicyQualifiers.java b/pki/base/util/src/netscape/security/x509/PolicyQualifiers.java new file mode 100644 index 00000000..69d0d015 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/PolicyQualifiers.java @@ -0,0 +1,103 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.util.Vector; +import java.io.IOException; +import netscape.security.util.*; + + +/** + * Represent the PolicyQualifiers. + * + * policyQualifiers ::= SEQUENCE SIZE (1..MAX) OF PolicyQualifierInfo + * + * @author Thomas Kwan + */ +public class PolicyQualifiers implements java.io.Serializable { + + private Vector mInfo = new Vector(); + + /** + * Create a PolicyQualifiers with the ObjectIdentifier. + * + * @param id the ObjectIdentifier for the policy id. + */ + public PolicyQualifiers() { + } + + /** + * Create the object from its Der encoded value. + * + * @param val the DER encoded value for the same. + */ + public PolicyQualifiers(DerValue val) throws IOException { + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for " + "PolicyQualifiers."); + } + while (val.data.available() != 0) { + DerValue pq = val.data.getDerValue(); + PolicyQualifierInfo info = new PolicyQualifierInfo(pq); + add(info); + } + } + + public void add(PolicyQualifierInfo info) { + mInfo.addElement(info); + } + + public int size() + { + return mInfo.size(); + } + + public PolicyQualifierInfo getInfoAt(int i) { + return (PolicyQualifierInfo)mInfo.elementAt(i); + } + + /** + * Returns a printable representation of the CertificatePolicyId. + */ + public String toString() { + String s = "PolicyQualifiers: ["; + for (int i = 0; i < mInfo.size(); i++) { + PolicyQualifierInfo pq = (PolicyQualifierInfo)mInfo.elementAt(i); + s = s + pq.toString(); + } + s = s + "]\n"; + + return (s); + } + + /** + * Write the PolicyQualifiers to the DerOutputStream. + * + * @param out the DerOutputStream to write the object to. + * @exception IOException on errors. + */ + public void encode(DerOutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + + for (int i = 0; i < mInfo.size(); i++) { + PolicyQualifierInfo pq = (PolicyQualifierInfo)mInfo.elementAt(i); + pq.encode(tmp); + } + + out.write(DerValue.tag_Sequence, tmp); + } +} diff --git a/pki/base/util/src/netscape/security/x509/PrintableConverter.java b/pki/base/util/src/netscape/security/x509/PrintableConverter.java new file mode 100644 index 00000000..38457efd --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/PrintableConverter.java @@ -0,0 +1,125 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.*; +import sun.io.CharToByteConverter; + +import netscape.security.util.*; + +/** + * A AVAValueConverter that converts a Printable String attribute to a DerValue + * and vice versa. An example an attribute that is a printable string is "C". + * + * @see ASN1CharStrConvMap + * @see AVAValueConverter + * + * @author Lily Hsiao, Slava Galperin at Netscape Communications, Inc. + */ + +public class PrintableConverter implements AVAValueConverter +{ + // public constructors. + + public PrintableConverter() + { + } + + /** + * Converts a string with ASN.1 Printable characters to a DerValue. + * + * @param valueString a string with Printable characters. + * + * @return a DerValue. + * + * @exception IOException if a Printable CharToByteConverter is not + * available for the conversion. + */ + public DerValue getValue(String valueString) + throws IOException + { + return getValue(valueString, null); + } + + public DerValue getValue(String valueString, byte[] encodingOrder) + throws IOException + { + CharToByteConverter printable; + byte[] bbuf = new byte[valueString.length()]; + try { + printable = ASN1CharStrConvMap.getDefault().getCBC( + DerValue.tag_PrintableString); + if (printable == null) { + throw new IOException("No CharToByteConverter for printable"); + } + printable.convert(valueString.toCharArray(), 0, + valueString.length(), bbuf, 0, bbuf.length); + } + catch (java.io.CharConversionException e) { + throw new IllegalArgumentException( + "Invalid Printable String AVA Value"); + } + catch (InstantiationException e) { + throw new IOException("Cannot instantiate CharToByteConverter"); + } + catch (IllegalAccessException e) { + throw new IOException("Cannot load CharToByteConverter"); + } + return new DerValue(DerValue.tag_PrintableString, bbuf); + } + + /** + * Converts a BER encoded value of PrintableString to a DER encoded value. + * Checks if the BER encoded value is a PrintableString. + * NOTE only DER encoded values are currently accepted on input. + * + * @param berStream A byte array of the BER encoded value. + * + * @return A DerValue. + * + * @exception IOException if the BER value cannot be converted to a + * PrintableString DER value. + */ + public DerValue getValue(byte[] berStream) + throws IOException + { + DerValue value = new DerValue(berStream); + if (value.tag != DerValue.tag_PrintableString) + throw new IOException("Invalid Printable String AVA Value"); + return value; + } + + /** + * Converts a DerValue of PrintableString to a java string with + * PrintableString characters. + * + * @param avaValue a DerValue. + * + * @return a string with PrintableString characters. + * + * @exception IOException if the DerValue is not a PrintableString i.e. + * The DerValue cannot be converted to a string + * with PrintableString characters. + */ + public String getAsString(DerValue avaValue) + throws IOException + { + return avaValue.getPrintableString(); + } + +} diff --git a/pki/base/util/src/netscape/security/x509/PrivateKeyUsageExtension.java b/pki/base/util/src/netscape/security/x509/PrivateKeyUsageExtension.java new file mode 100644 index 00000000..70848994 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/PrivateKeyUsageExtension.java @@ -0,0 +1,331 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.security.cert.CertificateException; +import java.security.cert.CertificateParsingException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.util.Date; +import java.util.Enumeration; + +import netscape.security.util.*; + +/** + * This class defines the Private Key Usage Extension. + * + * <p>The Private Key Usage Period extension allows the certificate issuer + * to specify a different validity period for the private key than the + * certificate. This extension is intended for use with digital + * signature keys. This extension consists of two optional components + * notBefore and notAfter. The private key associated with the + * certificate should not be used to sign objects before or after the + * times specified by the two components, respectively. + * + * <pre> + * PrivateKeyUsagePeriod ::= SEQUENCE { + * notBefore [0] GeneralizedTime OPTIONAL, + * notAfter [1] GeneralizedTime OPTIONAL } + * </pre> + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.12 + * @see Extension + * @see CertAttrSet + */ +public class PrivateKeyUsageExtension extends Extension +implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.extensions.PrivateKeyUsage"; + /** + * Sub attributes name for this CertAttrSet. + */ + public static final String NAME = "PrivateKeyUsage"; + public static final String NOT_BEFORE = "not_before"; + public static final String NOT_AFTER = "not_after"; + + // Private data members + private static final byte TAG_BEFORE = 0; + private static final byte TAG_AFTER = 1; + + private Date notBefore; + private Date notAfter; + + // Encode this extension value. + private void encodeThis() throws IOException { + DerOutputStream seq = new DerOutputStream(); + + DerOutputStream tagged = new DerOutputStream(); + if (notBefore != null) { + DerOutputStream tmp = new DerOutputStream(); + tmp.putGeneralizedTime(notBefore); + tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + false, TAG_BEFORE), tmp); + } + if (notAfter != null) { + DerOutputStream tmp = new DerOutputStream(); + tmp.putGeneralizedTime(notAfter); + tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + false, TAG_AFTER), tmp); + } + seq.write(DerValue.tag_Sequence, tagged); + extensionValue = seq.toByteArray(); + } + + /** + * The default constructor for PrivateKeyUsageExtension. + * + * @param notBefore the date/time before which the private key + * should not be used. + * @param notAfter the date/time after which the private key + * should not be used. + */ + public PrivateKeyUsageExtension(Date notBefore, Date notAfter) + throws IOException { + this.notBefore = notBefore; + this.notAfter = notAfter; + + this.extensionId = PKIXExtensions.PrivateKeyUsage_Id; + this.critical = false; + encodeThis(); + } + + /** + * Create the extension from the passed DER encoded value. + * + * @param critical true if the extension is to be treated as critical. + * @param value Array of DER encoded bytes of the actual value. + * + * @exception CertificateException on certificate parsing errors. + * @exception IOException on error. + */ + public PrivateKeyUsageExtension(Boolean critical, Object value) + throws CertificateException, IOException { + this.extensionId = PKIXExtensions.PrivateKeyUsage_Id; + this.critical = critical.booleanValue(); + + if (!(value instanceof byte[])) + throw new CertificateException("Illegal argument type"); + + int len = Array.getLength(value); + byte[] extValue = new byte[len]; + System.arraycopy(value, 0, extValue, 0, len); + + this.extensionValue = extValue; + DerInputStream str = new DerInputStream(extValue); + DerValue[] seq = str.getSequence(2); + + // NB. this is always encoded with the IMPLICIT tag + // The checks only make sense if we assume implicit tagging, + // with explicit tagging the form is always constructed. + for (int i = 0; i < seq.length; i++) { + DerValue opt = seq[i]; + + if (opt.isContextSpecific((byte)TAG_BEFORE) && + !opt.isConstructed()) { + if (notBefore != null) { + throw new CertificateParsingException( + "Duplicate notBefore in PrivateKeyUsage."); + } + opt.resetTag(DerValue.tag_GeneralizedTime); + str = new DerInputStream(opt.toByteArray()); + notBefore = str.getGeneralizedTime(); + + } else if (opt.isContextSpecific((byte)TAG_AFTER) && + !opt.isConstructed()) { + if (notAfter != null) { + throw new CertificateParsingException( + "Duplicate notAfter in PrivateKeyUsage."); + } + opt.resetTag(DerValue.tag_GeneralizedTime); + str = new DerInputStream(opt.toByteArray()); + notAfter = str.getGeneralizedTime(); + } else + throw new IOException("Invalid encoding of " + + "PrivateKeyUsageExtension"); + } + } + + /** + * Return the printable string. + */ + public String toString() { + return(super.toString() + + "PrivateKeyUsage: [From: " + + ((notBefore == null) ? "" : notBefore.toString()) + + ", To: " + + ((notAfter == null) ? "" : notAfter.toString()) + "]\n"); + } + + /** + * Return notBefore date + */ + public Date getNotBefore() { + return(notBefore); + } + + /** + * Return notAfter date + */ + public Date getNotAfter() { + return(notAfter); + } + + /** + * Verify that that the current time is within the validity period. + * + * @exception CertificateExpiredException if the certificate has expired. + * @exception CertificateNotYetValidException if the certificate is not + * yet valid. + */ + public void valid() + throws CertificateNotYetValidException, CertificateExpiredException { + Date now = new Date(); + valid(now); + } + + /** + * Verify that that the passed time is within the validity period. + * + * @exception CertificateExpiredException if the certificate has expired + * with respect to the <code>Date</code> supplied. + * @exception CertificateNotYetValidException if the certificate is not + * yet valid with respect to the <code>Date</code> supplied. + * + */ + public void valid(Date now) + throws CertificateNotYetValidException, CertificateExpiredException { + /* + * we use the internal Dates rather than the passed in Date + * because someone could override the Date methods after() + * and before() to do something entirely different. + */ + if (notBefore.after(now)) { + throw new CertificateNotYetValidException("NotBefore: " + + notBefore.toString()); + } + if (notAfter.before(now)) { + throw new CertificateExpiredException("NotAfter: " + + notAfter.toString()); + } + } + + /** + * Write the extension to the OutputStream. + * + * @param out the OutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (extensionValue == null) { + extensionId = PKIXExtensions.PrivateKeyUsage_Id; + critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Decode the extension from the InputStream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception CertificateException on decoding errors. + */ + public void decode(InputStream in) throws CertificateException { + throw new CertificateException("Method not to be called directly."); + } + + /** + * Set the attribute value. + * @exception CertificateException on attribute handling errors. + */ + public void set(String name, Object obj) + throws CertificateException { + clearValue(); + if (!(obj instanceof Date)) { + throw new CertificateException("Attribute must be of type Date."); + } + if (name.equalsIgnoreCase(NOT_BEFORE)) { + notBefore = (Date)obj; + } else if (name.equalsIgnoreCase(NOT_AFTER)) { + notAfter = (Date)obj; + } else { + throw new CertificateException("Attribute name not recognized by" + + " CertAttrSet:PrivateKeyUsage."); + } + } + + /** + * Get the attribute value. + * @exception CertificateException on attribute handling errors. + */ + public Object get(String name) throws CertificateException { + if (name.equalsIgnoreCase(NOT_BEFORE)) { + return (new Date(notBefore.getTime())); + } else if (name.equalsIgnoreCase(NOT_AFTER)) { + return (new Date(notAfter.getTime())); + } else { + throw new CertificateException("Attribute name not recognized by" + + " CertAttrSet:PrivateKeyUsage."); + } + } + + /** + * Delete the attribute value. + * @exception CertificateException on attribute handling errors. + */ + public void delete(String name) throws CertificateException { + if (name.equalsIgnoreCase(NOT_BEFORE)) { + notBefore = null; + } else if (name.equalsIgnoreCase(NOT_AFTER)) { + notAfter = null; + } else { + throw new CertificateException("Attribute name not recognized by" + + " CertAttrSet:PrivateKeyUsage."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(NOT_BEFORE); + elements.addElement(NOT_AFTER); + + return(elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return(NAME); + } +} diff --git a/pki/base/util/src/netscape/security/x509/Qualifier.java b/pki/base/util/src/netscape/security/x509/Qualifier.java new file mode 100644 index 00000000..ef13fc10 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/Qualifier.java @@ -0,0 +1,57 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import netscape.security.util.*; + + +/** + * Represent the Qualifier. + * + * Qualifier ::= CHOICE { + * cPRuri CPSuri, + * userNotice UserNotice + * } + * + * @author Thomas Kwan + */ +public class Qualifier implements java.io.Serializable { + + /** + * Create a PolicyQualifierInfo + * + * @param id the ObjectIdentifier for the policy id. + */ + public Qualifier() { + } + + public Qualifier(DerValue val) throws IOException { + // needs to override this + } + + /** + * Write the PolicyQualifier to the DerOutputStream. + * + * @param out the DerOutputStream to write the object to. + * @exception IOException on errors. + */ + public void encode(DerOutputStream out) throws IOException { + // needs to override this + } +} diff --git a/pki/base/util/src/netscape/security/x509/RDN.java b/pki/base/util/src/netscape/security/x509/RDN.java new file mode 100644 index 00000000..43ce490d --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/RDN.java @@ -0,0 +1,314 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Vector; +import java.util.Enumeration; + +import netscape.security.util.*; + + +/** + * RDNs are a set of {attribute = value} assertions. Some of those + * attributes are "distinguished" (unique w/in context). Order is + * never relevant. + * + * Some X.500 names include only a single distinguished attribute + * per RDN. This style is currently common. + * + * Note that DER-encoded RDNs sort AVAs by assertion OID ... so that + * when we parse this data we don't have to worry about canonicalizing + * it, but we'll need to sort them when we expose the RDN class more. + * + * @see X500Name + * @see AVA + * @see LdapDNStrConverter + */ + +public +class RDN +{ + // public constructors + + /** + * Constructs a RDN from a Ldap DN String with one RDN component + * using the global default LdapDNStrConverter. + * @see LdapDNStrConverter + * @param rdnString a Ldap DN string with one RDN component, e.g. as + * defined in RFC1779. + * @exception IOException if error occurs while parsing the string. + */ + public RDN (String rdnString) + throws IOException + { + RDN rdn = LdapDNStrConverter.getDefault().parseRDN(rdnString); + assertion = rdn.getAssertion(); + } + + /** + * Like RDN(String) with a DER encoding order given as argument for + * Directory Strings. + */ + public RDN (String rdnString, byte[] tags) + throws IOException + { + RDN rdn = LdapDNStrConverter.getDefault().parseRDN(rdnString, tags); + assertion = rdn.getAssertion(); + } + + /** + * Constructs a RDN from a Ldap DN string with one RDN component + * using the specified Ldap DN Str converter. + * For example, RFC1779StrConverter can be passed to parse a Ldap + * DN string in RFC1779 format. + * @see LdapDNStrConverter + * @param rdnString Ldap DN string. + * @param ldapDNStrConverter a LdapDNStrConverter. + */ + public RDN (String rdnString, LdapDNStrConverter ldapDNStrConverter) + throws IOException + { + RDN rdn = ldapDNStrConverter.parseRDN(rdnString); + assertion = rdn.getAssertion(); + } + + /** + * Constructs a RDN from a DerValue. + * @param set Der value of a set of AVAs. + */ + public RDN (DerValue set) throws IOException + { + if (set.tag != DerValue.tag_Set) + throw new CertParseError ("X500 RDN"); + + int j_max = 50; // XXX j_max = f(data)!! + int j; + int i; + + AVA[] avas = new AVA [j_max]; + +// create a temporary array big enough for a huge set of AVA's + for (j = 0; j < j_max; j++) { + avas [j] = new AVA (set.data); + if (set.data.available() == 0) break; + } + +// copy the elements into it + if (j >= j_max-1) { + assertion = new AVA[j+1]; + } + else { + assertion = new AVA[j+1]; + for (i = 0; i<(j+1); i++) { + assertion[i] = avas[i]; + } + } + + /* + if (set.data.available () != 0) + // throw new CertParseError ("X500 RDN 2"); + System.out.println (" ... RDN parse, ignored bytes = " + + set.data.available ()); + */ + } + + /** + * Constructs a RDN from a Der Input Stream. + * @param in a Der Input Stream. + */ + public RDN (DerInputStream in) throws IOException + { + /* an RDN is a SET of avas */ + DerValue avaset[] = in.getSet(1); + int i; + assertion = new AVA[avaset.length]; + for (i = 0; i < assertion.length; i++) + assertion[i] = new AVA(avaset[i].data); + } + + /** + * Constructs a RDN from an array of AVA. + * + * @param avas a AVA Array. + */ + public RDN(AVA avas[]) + { + assertion = (AVA[])avas.clone(); + } + + /** + * convenience method. + */ + public RDN(Vector avaVector) + { + int size = avaVector.size(); + assertion = new AVA[size]; + for (int i = 0; i < size; i++) { + assertion[i] = (AVA)avaVector.elementAt(i); + } + } + + /** + * returns an array of AVA in the RDN. + * @return array of AVA in this RDN. + */ + public AVA[] getAssertion() + { + return (AVA[])assertion.clone(); + } + + /** + * returns the number of AVAs in the RDN. + * @return number of AVAs in this RDN. + */ + public int getAssertionLength() + { + return assertion.length; + } + + private AVA assertion []; + + private class AVAEnumerator implements Enumeration + { + private int index; + + public AVAEnumerator() { index = 0; } + + public boolean hasMoreElements() + { + return (index < assertion.length); + } + + public Object nextElement() + { + AVA ava = null; + if (index >= assertion.length) + return null; + return assertion[index++]; + } + } + + + // other public methods. + + /** + * Checks if this RDN is the same as another by comparing the AVAs + * in the RDNs. + * + * @param other the other RDN. + * @return true iff the other RDN is the same. + */ + public boolean equals (RDN other) + { + int i; + + if (other == this) + return true; + if (assertion.length != other.assertion.length) + return false; + + for (i = 0; i < assertion.length; i++) + if (!assertion [i].equals (other.assertion [i])) + return false; + + return true; + } + + DerValue findAttribute (ObjectIdentifier oid) + { + int i; + + for (i = 0; i < assertion.length; i++) + if (assertion [i].oid.equals (oid)) + return assertion [i].value; + return null; + } + + /** + * Encodes this RDN to a Der output stream. + * + * @param out the Der Output Stream. + */ + public void encode(DerOutputStream out) throws IOException + { + DerOutputStream tmp = new DerOutputStream (); + int i; + + for (i = 0; i < assertion.length; i++) + assertion [i].encode (tmp); + out.write (DerValue.tag_Set, tmp); + } + + /** + * returns an enumeration of AVAs that make up this RDN. + * @return an enumeration of AVAs that make up this RDN. + */ + public Enumeration getAVAs() + { + return new AVAEnumerator(); + } + + /** + * Returns a Ldap DN string with one RDN component using the + * global default LdapDNStrConverter. + * @see LdapDNStrConverter + * @return the Ldap DN String of this RDN. + * @exception IOException if an error occurs during the conversion. + */ + public String toLdapDNString () + throws IOException + { + return LdapDNStrConverter.getDefault().encodeRDN(this); + } + + /** + * Returns a Ldap DN String with this RDN component using the specified + * LdapDNStrConverter. + * @see LdapDNStrConverter + * @param ldapDNStrConverter a LdapDNStrConverter. + * @return a Ldap DN String. + * @exception IOException if an error occurs in the conversion. + */ + public String toLdapDNString (LdapDNStrConverter ldapDNStrConverter) + throws IOException + { + return ldapDNStrConverter.encodeRDN(this); + } + + /** + * Returns a Ldap DN string with this RDN component using the global + * default LdapDNStrConverter. + * @see LdapDNStrConverter + * @return the Ldap DN String with this RDN component, null if an error + * occurs in the conversion. + */ + public String toString() + { + String s; + try { + s = toLdapDNString(); + } + catch (IOException e) { + return null; + } + return s; + } + +} diff --git a/pki/base/util/src/netscape/security/x509/RFC1779StrConverter.java b/pki/base/util/src/netscape/security/x509/RFC1779StrConverter.java new file mode 100644 index 00000000..d7c19482 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/RFC1779StrConverter.java @@ -0,0 +1,106 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.*; + +import netscape.security.util.*; + +/** + * Converts a RFC 1779 string to a X500Name, RDN or AVA object and vice versa. + * + * @see LdapDNStrConverter + * @see LdapV3DNStrConverter + * + * @author Lily Hsiao, Slava Galperin at Netscape Communications, Inc. + */ + +public class RFC1779StrConverter extends LdapV3DNStrConverter +{ + // + // Constructors. + // + + /** + * Constructs a RFC1779StrConverter using the global default + * X500NameAttrMap and accepts OIDs not listed in the attribute map. + */ + public RFC1779StrConverter() + { + super(); + } + + /** + * Constructs a RFC1779StrConverter using the specified X500NameAttrMap + * and boolean for whether to accept OIDs not in the X500NameAttrMap. + * + * @param attributeMap A X500NameAttrMap to use for this converter. + * @param doAcceptUnknownOids Accept unregistered attributes, i.e. OIDs + * not in the map). + */ + public RFC1779StrConverter(X500NameAttrMap attributeMap, + boolean doAcceptUnknownOids) + { + super(attributeMap, doAcceptUnknownOids); + } + + // + // overriding methods. + // + + /** + * Converts a OID to a attribute keyword in a Ldap DN string or + * to a "OID.1.2.3.4" string syntax as defined in RFC1779. + * + * @param oid an ObjectIdentifier. + * + * @return a attribute keyword or "OID.1.2.3.4" string. + * + * @exception IOException if an error occurs during the conversion. + */ + public String encodeOID(ObjectIdentifier oid) + throws IOException + { + String keyword = attrMap.getName(oid); + if (keyword == null) + if (!acceptUnknownOids) + throw new IllegalArgumentException("Unrecognized OID"); + else + keyword = "OID" + "." + oid.toString(); + return keyword; + } + + /** + * Converts a attribute value as a DerValue to a string in a + * RFC1779 Ldap DN string. + * + * @param attrValue an attribute value. + * @param oid ObjectIdentifier for the attribute. + * @return a string in RFC1779 syntax. + * @exception IOException if an error occurs during the conversion. + */ + public String encodeValue(DerValue attrValue, ObjectIdentifier oid) + throws IOException + { + String s = super.encodeValue(attrValue, oid); + if (s.indexOf('\n') != -1) + return "\""+s+"\""; + else + return s; + } +} diff --git a/pki/base/util/src/netscape/security/x509/RFC822Name.java b/pki/base/util/src/netscape/security/x509/RFC822Name.java new file mode 100644 index 00000000..19a73a30 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/RFC822Name.java @@ -0,0 +1,81 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; + +import netscape.security.util.*; + +/** + * This class implements the RFC822Name as required by the GeneralNames + * ASN.1 object. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.3 + * @see GeneralName + * @see GeneralNames + * @see GeneralNameInterface + */ +public class RFC822Name implements GeneralNameInterface +{ + private String name; + + /** + * Create the RFC822Name object from the passed encoded Der value. + * + * @param derValue the encoded DER RFC822Name. + * @exception IOException on error. + */ + public RFC822Name(DerValue derValue) throws IOException { + name = derValue.getIA5String(); + } + + /** + * Create the RFC822Name object with the specified name. + * + * @param name the RFC822Name. + */ + public RFC822Name(String name) { + this.name = name; + } + + /** + * Return the type of the GeneralName. + */ + public int getType() { + return (GeneralNameInterface.NAME_RFC822); + } + + /** + * Encode the RFC822 name into the DerOutputStream. + * + * @param out the DER stream to encode the RFC822Name to. + * @exception IOException on encoding errors. + */ + public void encode(DerOutputStream out) throws IOException { + out.putIA5String(name); + } + + /** + * Convert the name into user readable string. + */ + public String toString() { + return ("RFC822Name: " + name); + } +} diff --git a/pki/base/util/src/netscape/security/x509/ReasonFlags.java b/pki/base/util/src/netscape/security/x509/ReasonFlags.java new file mode 100755 index 00000000..df634423 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/ReasonFlags.java @@ -0,0 +1,260 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.util.Enumeration; + +import netscape.security.util.*; + +/** + * Represent the CRL Reason Flags. + * + * <p>This extension, if present, defines the identifies + * the reason for the certificate revocation. + * + * @author Hemma Prafullchandra + * @version 1.3 + * @see Extension + * @see CertAttrSet + */ +public class ReasonFlags { + + /** + * Reasons + */ + public static final String UNUSED = "unused"; + public static final String KEY_COMPROMISE = "key_compromise"; + public static final String CA_COMPROMISE = "ca_compromise"; + public static final String AFFLIATION_CHANGED = "affliation_changed"; + public static final String SUPERSEDED = "superseded"; + public static final String CESSATION_OF_OPERATION + = "cessation_of_operation"; + public static final String CERTIFICATION_HOLD = "certification_hold"; + + // Private data members + private boolean[] bitString; + + /** + * Check if bit is set. + * + * @param position the position in the bit string to check. + */ + private boolean isSet(int position) { + return bitString[position]; + } + + /** + * Set the bit at the specified position. + */ + private void set(int position, boolean val) { + // enlarge bitString if necessary + if (position >= bitString.length) { + boolean[] tmp = new boolean[position+1]; + System.arraycopy(bitString, 0, tmp, 0, bitString.length); + bitString = tmp; + } + bitString[position] = val; + } + + /** + * Create a ReasonFlags with the passed bit settings. + * + * @param reasons the bits to be set for the ReasonFlags. + */ + public ReasonFlags(byte[] reasons) { + bitString = new BitArray(reasons.length*8, reasons).toBooleanArray(); + } + + /** + * Create a ReasonFlags with the passed bit settings. + * + * @param reasons the bits to be set for the ReasonFlags. + */ + public ReasonFlags(boolean[] reasons) { + this.bitString = reasons; + } + + /** + * Create a ReasonFlags with the passed bit settings. + * + * @param reasons the bits to be set for the ReasonFlags. + */ + public ReasonFlags(BitArray reasons) { + this.bitString = reasons.toBooleanArray(); + } + + /** + * Create the object from the passed DER encoded value. + * + * @param in the DerInputStream to read the ReasonFlags from. + * @exception IOException on decoding errors. + */ + public ReasonFlags(DerInputStream in) throws IOException { + DerValue derVal = in.getDerValue(); + this.bitString = derVal.getUnalignedBitString(true).toBooleanArray(); + } + + /** + * Create the object from the passed DER encoded value. + * + * @param derVal the DerValue decoded from the stream. + * @exception IOException on decoding errors. + */ + public ReasonFlags(DerValue derVal) throws IOException { + this.bitString = derVal.getUnalignedBitString(true).toBooleanArray(); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (!(obj instanceof Boolean)) { + throw new IOException("Attribute must be of type Boolean."); + } + boolean val = ((Boolean)obj).booleanValue(); + if (name.equalsIgnoreCase(UNUSED)) { + set(0,val); + } else if (name.equalsIgnoreCase(KEY_COMPROMISE)) { + set(1,val); + } else if (name.equalsIgnoreCase(CA_COMPROMISE)) { + set(2,val); + } else if (name.equalsIgnoreCase(AFFLIATION_CHANGED)) { + set(3,val); + } else if (name.equalsIgnoreCase(SUPERSEDED)) { + set(4,val); + } else if (name.equalsIgnoreCase(CESSATION_OF_OPERATION)) { + set(5,val); + } else if (name.equalsIgnoreCase(CERTIFICATION_HOLD)) { + set(6,val); + } else { + throw new IOException("Name not recognized by ReasonFlags"); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(UNUSED)) { + return new Boolean(isSet(0)); + } else if (name.equalsIgnoreCase(KEY_COMPROMISE)) { + return new Boolean(isSet(1)); + } else if (name.equalsIgnoreCase(CA_COMPROMISE)) { + return new Boolean(isSet(2)); + } else if (name.equalsIgnoreCase(AFFLIATION_CHANGED)) { + return new Boolean(isSet(3)); + } else if (name.equalsIgnoreCase(SUPERSEDED)) { + return new Boolean(isSet(4)); + } else if (name.equalsIgnoreCase(CESSATION_OF_OPERATION)) { + return new Boolean(isSet(5)); + } else if (name.equalsIgnoreCase(CERTIFICATION_HOLD)) { + return new Boolean(isSet(6)); + } else { + throw new IOException("Name not recognized by ReasonFlags"); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(UNUSED)) { + set(0,false); + } else if (name.equalsIgnoreCase(KEY_COMPROMISE)) { + set(1,false); + } else if (name.equalsIgnoreCase(CA_COMPROMISE)) { + set(2,false); + } else if (name.equalsIgnoreCase(AFFLIATION_CHANGED)) { + set(3,false); + } else if (name.equalsIgnoreCase(SUPERSEDED)) { + set(4,false); + } else if (name.equalsIgnoreCase(CESSATION_OF_OPERATION)) { + set(5,false); + } else if (name.equalsIgnoreCase(CERTIFICATION_HOLD)) { + set(6,false); + } else { + throw new IOException("Name not recognized by ReasonFlags"); + } + } + + /** + * Returns a printable representation of the ReasonFlags. + */ + public String toString() { + String s = super.toString() + "Reason Flags [\n"; + + try { + if (isSet(0)) { + s += " Unused\n"; + } + if (isSet(1)) { + s += " Key Compromise\n"; + } + if (isSet(2)) { + s += " CA_Compromise\n"; + } + if (isSet(3)) { + s += " Affiliation_Changed\n"; + } + if (isSet(4)) { + s += " Superseded\n"; + } + if (isSet(5)) { + s += " Cessation Of Operation\n"; + } + if (isSet(6)) { + s += " Certificate Hold\n"; + } + } catch (ArrayIndexOutOfBoundsException ex) {} + + s += "]\n"; + + return (s); + } + + /** + * Write the extension to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(DerOutputStream out) throws IOException { + out.putUnalignedBitString(new BitArray(this.bitString)); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements () { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(UNUSED); + elements.addElement(KEY_COMPROMISE); + elements.addElement(CA_COMPROMISE); + elements.addElement(AFFLIATION_CHANGED); + elements.addElement(SUPERSEDED); + elements.addElement(CESSATION_OF_OPERATION); + elements.addElement(CERTIFICATION_HOLD); + + return (elements.elements()); + } +} diff --git a/pki/base/util/src/netscape/security/x509/RevocationReason.java b/pki/base/util/src/netscape/security/x509/RevocationReason.java new file mode 100644 index 00000000..3c3547d8 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/RevocationReason.java @@ -0,0 +1,102 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.util.Enumeration; + +import netscape.security.util.*; + +/** + * Represent the enumerated type used in CRLReason Extension of CRL entry. + * + * + * @author galperin + * @version $Revision: 14564 $, $Date: 2007-05-01 10:40:13 -0700 (Tue, 01 May 2007) $ + */ + +public final class RevocationReason { + /** + * Reasons + */ + public static final RevocationReason UNSPECIFIED = new RevocationReason(0); + public static final RevocationReason KEY_COMPROMISE = new RevocationReason(1); + public static final RevocationReason CA_COMPROMISE = new RevocationReason(2); + public static final RevocationReason AFFILIATION_CHANGED = new RevocationReason(3); + public static final RevocationReason SUPERSEDED = new RevocationReason(4); + public static final RevocationReason CESSATION_OF_OPERATION + = new RevocationReason(5); + public static final RevocationReason CERTIFICATE_HOLD = new RevocationReason(6); + public static final RevocationReason REMOVE_FROM_CRL = new RevocationReason(8); + + // Private data members + private int mReason; + + /** + * Create a RevocationReason with the passed integer value. + * + * @param reason integer value of the enumeration alternative. + */ + private RevocationReason(int reason){ + this.mReason = reason; + } + + public int toInt() { + return mReason; + } + + public static RevocationReason fromInt(int reason) { + if (reason == UNSPECIFIED.mReason) return UNSPECIFIED; + if (reason == KEY_COMPROMISE.mReason) return KEY_COMPROMISE; + if (reason == CA_COMPROMISE.mReason) return CA_COMPROMISE; + if (reason == AFFILIATION_CHANGED.mReason) return AFFILIATION_CHANGED; + if (reason == SUPERSEDED.mReason) return SUPERSEDED; + if (reason == CESSATION_OF_OPERATION.mReason) return CESSATION_OF_OPERATION; + if (reason == CERTIFICATE_HOLD.mReason) return CERTIFICATE_HOLD; + if (reason == REMOVE_FROM_CRL.mReason) return REMOVE_FROM_CRL; + return null; + } + + public boolean equals(Object other) { + if (this == other) + return true; + else if (other instanceof RevocationReason) + return ((RevocationReason)other).mReason == mReason; + else + return false; + } + + public int hashCode() { + return mReason; + } + + public String toString() { + if (equals(UNSPECIFIED)) return "Unspecified"; + if (equals(KEY_COMPROMISE)) return "Key_Compromise"; + if (equals(CA_COMPROMISE)) return "CA_Compromise"; + if (equals(AFFILIATION_CHANGED)) return "Affiliation_Changed"; + if (equals(SUPERSEDED)) return "Superseded"; + if (equals(CESSATION_OF_OPERATION)) return "Cessation_of_Operation"; + if (equals(CERTIFICATE_HOLD)) return "Certificate_Hold"; + if (equals(REMOVE_FROM_CRL)) return "Remove_from_CRL"; + return "[UNDEFINED]"; + } +} diff --git a/pki/base/util/src/netscape/security/x509/RevokedCertImpl.java b/pki/base/util/src/netscape/security/x509/RevokedCertImpl.java new file mode 100755 index 00000000..785a5d79 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/RevokedCertImpl.java @@ -0,0 +1,440 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.Serializable; +import java.io.InputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.security.cert.CRLException; +import java.security.cert.CertificateException; +import java.math.BigInteger; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.Set; + +import netscape.security.util.*; +import java.io.ByteArrayOutputStream; + +/** + * <p>Abstract class for a revoked certificate in a CRL. + * This class is for each entry in the <code>revokedCertificates</code>, + * so it deals with the inner <em>SEQUENCE</em>. + * The ASN.1 definition for this is: + * <pre> + * revokedCertificates SEQUENCE OF SEQUENCE { + * userCertificate CertificateSerialNumber, + * revocationDate ChoiceOfTime, + * crlEntryExtensions Extensions OPTIONAL + * -- if present, must be v2 + * } OPTIONAL + * + * CertificateSerialNumber ::= INTEGER + * + * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension + * + * Extension ::= SEQUENCE { + * extnId OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * extnValue OCTET STRING + * -- contains a DER encoding of a value + * -- of the type registered for use with + * -- the extnId object identifier value + * } + * </pre> + * + * @author Hemma Prafullchandra + * @version 1.6 97/12/10 + */ + +public class RevokedCertImpl extends RevokedCertificate + implements Serializable{ + + private SerialNumber serialNumber; + private Date revocationDate; + private CRLExtensions extensions = null; + private byte[] revokedCert; + private final static boolean isExplicit = false; + + /** + * Default constructor. + */ + public RevokedCertImpl() { } + + /** + * Constructs a revoked certificate entry using the + * serial number and revocation date. + * + * @param num the serial number of the revoked certificate. + * @param date the Date on which revocation took place. + */ + public RevokedCertImpl(BigInteger num, Date date) { + this.serialNumber = new SerialNumber(num); + this.revocationDate = date; + } + + /** + * Constructs a revoked certificate entry using the + * serial number, revocation date and the entry + * extensions. + * + * @param num the serial number of the revoked certificate. + * @param date the Date on which revocation took place. + * @param crlEntryExts the extensions for this entry. + */ + public RevokedCertImpl(BigInteger num, Date date, + CRLExtensions crlEntryExts) { + this.serialNumber = new SerialNumber(num); + this.revocationDate = date; + this.extensions = crlEntryExts; + } + + public byte[] getEncoded() throws CRLException { + // XXX NOT IMPLEMENTED + if (revokedCert == null) { + DerOutputStream os = new DerOutputStream(); + try { + encode(os); + } catch (Exception e) { + // revokedCert = null; + } + revokedCert = os.toByteArray(); + } + return revokedCert; + } + + public boolean hasUnsupportedCriticalExtension() { + // XXX NOT IMPLEMENTED + return true; + } + + /** + * Sets extensions for this impl. + * + * @param crlEntryExts CRLExtensions + */ + public void setExtensions(CRLExtensions crlEntryExts) { + this.extensions = crlEntryExts; + } + + /** + * Unmarshals a revoked certificate from its encoded form. + * + * @param revokedCert the encoded bytes. + * @exception CRLException on parsing errors. + * @exception X509ExtensionException on extension handling errors. + */ + public RevokedCertImpl(byte[] revokedCert) + throws CRLException, X509ExtensionException { + try { + DerValue derValue = new DerValue(revokedCert); + parse(derValue); + } catch (IOException e) { + throw new CRLException("Parsing error: " + e.toString()); + } + } + + /** + * Unmarshals a revoked certificate from its encoded form. + * + * @param derValue the DER value containing the revoked certificate. + * @exception CRLException on parsing errors. + * @exception X509ExtensionException on extension handling errors. + */ + public RevokedCertImpl(DerValue derValue) + throws CRLException, X509ExtensionException { + parse(derValue); + } + + /** + * Returns true if this revoked certificate entry has + * extensions, otherwise false. + * + * @return true if this CRL entry has extensions, otherwise + * false. + */ + public boolean hasExtensions() { + if (extensions == null) + return false; + else + return true; + } + + /** + * Decode a revoked certificate from an input stream. + * + * @param inStrm an input stream holding at least one revoked + * certificate + * @exception CRLException on parsing errors. + * @exception X509ExtensionException on extension handling errors. + */ + public void decode(InputStream inStrm) + throws CRLException, X509ExtensionException { + try { + DerValue derValue = new DerValue(inStrm); + parse(derValue); + } catch (IOException e) { + throw new CRLException("Parsing error: " + e.toString()); + } + } + + /** + * Encodes the revoked certificate to an output stream. + * + * @param outStrm an output stream to which the encoded revoked + * certificate is written. + * @exception CRLException on encoding errors. + * @exception X509ExtensionException on extension handling errors. + */ + public void encode(DerOutputStream outStrm) + throws CRLException, X509ExtensionException { + try { + if (revokedCert == null) { + DerOutputStream tmp = new DerOutputStream(); + // sequence { serialNumber, revocationDate, extensions } + serialNumber.encode(tmp); + + // from 2050 should encode GeneralizedTime + tmp.putUTCTime(revocationDate); + + if (extensions != null) + extensions.encode(tmp, isExplicit); + + DerOutputStream seq = new DerOutputStream(); + seq.write(DerValue.tag_Sequence, tmp); + + revokedCert = seq.toByteArray(); + } + outStrm.write(revokedCert); + } catch (IOException e) { + throw new CRLException("Encoding error: " + e.toString()); + } + } + + /** + * Gets the serial number for this RevokedCertificate, + * the <em>userCertificate</em>. + * + * @return the serial number. + */ + public BigInteger getSerialNumber() { + return ((BigInt)serialNumber.getNumber()).toBigInteger(); + } + + /** + * Gets the revocation date for this RevokedCertificate, + * the <em>revocationDate</em>. + * + * @return the revocation date. + */ + public Date getRevocationDate() { + return (new Date(revocationDate.getTime())); + } + + /** + * Returns extensions for this impl. + * + * @return the CRLExtensions + */ + public CRLExtensions getExtensions() { + return extensions; + } + + + /** + * Returns a printable string of this revoked certificate. + * + * @return value of this revoked certificate in a printable form. + */ + public String toString() { + StringBuffer sb = new StringBuffer(); + + sb.append(serialNumber.toString()); + sb.append(" On: " + revocationDate.toString()); + if (extensions != null) { + sb.append("\n"); + for (int i = 0; i < extensions.size(); i++) + sb.append("Entry Extension[" + i + "]: " + + ((Extension)(extensions.elementAt(i))).toString()); + } + sb.append("\n"); + return (sb.toString()); + } + + /** + * Gets a Set of the extension(s) marked CRITICAL in the + * RevokedCertificate by OID strings. + * + * @return a set of the extension oid strings in the + * Object that are marked critical. + */ + public Set getCriticalExtensionOIDs() { + if (extensions == null) + return null; + ArraySet extSet = new ArraySet(); + Extension ex; + for (Enumeration e = extensions.getElements(); + e.hasMoreElements();) { + ex = (Extension)e.nextElement(); + if (ex.isCritical()) + extSet.add(((ObjectIdentifier)ex.getExtensionId()).toString()); + } + return extSet; + } + + /** + * Gets a Set of the extension(s) marked NON-CRITICAL in the + * RevokedCertificate by OID strings. + * + * @return a set of the extension oid strings in the + * Object that are marked critical. + */ + public Set getNonCriticalExtensionOIDs() { + if (extensions == null) + return null; + ArraySet extSet = new ArraySet(); + Extension ex; + for (Enumeration e = extensions.getElements(); + e.hasMoreElements();) { + ex = (Extension)e.nextElement(); + if ( ! ex.isCritical()) + extSet.add(((ObjectIdentifier)ex.getExtensionId()).toString()); + } + return extSet; + } + + /** + * Gets the DER encoded OCTET string for the extension value + * (<em>extnValue</em>) identified by the passed in oid String. + * The <code>oid</code> string is + * represented by a set of positive whole number separated + * by ".", that means,<br> + * <positive whole number>.<positive whole number>.<positive + * whole number>.<...> + * + * @param oid the Object Identifier value for the extension. + * @return the DER encoded octet string of the extension value. + */ + public byte[] getExtensionValue(String oid) { + if (extensions == null) + return null; + try { + String extAlias = OIDMap.getName(new ObjectIdentifier(oid)); + Extension crlExt = null; + + if (extAlias == null) { // may be unknown + ObjectIdentifier findOID = new ObjectIdentifier(oid); + Extension ex = null; + ObjectIdentifier inCertOID; + for (Enumeration e=extensions.getElements(); + e.hasMoreElements();) { + ex = (Extension)e.nextElement(); + inCertOID = ex.getExtensionId(); + if (inCertOID.equals(findOID)) { + crlExt = ex; + break; + } + } + } else + crlExt = extensions.get(extAlias); + if (crlExt == null) + return null; + byte[] extData = crlExt.getExtensionValue(); + if (extData == null) + return null; + + DerOutputStream out = new DerOutputStream(); + out.putOctetString(extData); + return out.toByteArray(); + } catch (Exception e) { + return null; + } + } + + private void parse(DerValue derVal) + throws CRLException, X509ExtensionException { + + if (derVal.tag != DerValue.tag_Sequence) { + throw new CRLException("Invalid encoded RevokedCertificate, " + + "starting sequence tag missing."); + } + if (derVal.data.available() == 0) + throw new CRLException("No data encoded for RevokedCertificates"); + + // serial number + try { + DerInputStream in = derVal.toDerInputStream(); + DerValue val = in.getDerValue(); + this.serialNumber = new SerialNumber(val); + } catch (IOException e) { + throw new CRLException("Parsing Serial Number error: " + + e.toString()); + } + + // revocationDate + try { + int nextByte = derVal.data.peekByte(); + if ((byte)nextByte == DerValue.tag_UtcTime) { + this.revocationDate = derVal.data.getUTCTime(); + } else if ((byte)nextByte == DerValue.tag_GeneralizedTime) { + this.revocationDate = derVal.data.getGeneralizedTime(); + } else { + throw new CRLException("Invalid encoding for RevokedCertificates"); + } + } catch (IOException e) { + throw new CRLException("Parsing Revocation Date error: " + + e.toString()); + } + + if (derVal.data.available() == 0) + return; // no extensions + + // crlEntryExtensions + try { + this.extensions = new CRLExtensions(derVal.toDerInputStream()); + } catch (IOException e) { + throw new CRLException("Parsing CRL Entry Extensions error: " + + e.toString()); + } + } + + /** + * Serialization write ... X.509 certificates serialize as + * themselves, and they're parsed when they get read back. + * (Actually they serialize as some type data from the + * serialization subsystem, then the cert data.) + */ + private synchronized void writeObject(ObjectOutputStream stream) + throws CRLException, X509ExtensionException, IOException { + DerOutputStream dos = new DerOutputStream(); + encode(dos); + dos.derEncode(stream); + } + + /** + * Serialization read ... X.509 certificates serialize as + * themselves, and they're parsed when they get read back. + */ + private synchronized void readObject(ObjectInputStream stream) + throws CRLException, X509ExtensionException, IOException { + decode(stream); + } + +} diff --git a/pki/base/util/src/netscape/security/x509/RevokedCertificate.java b/pki/base/util/src/netscape/security/x509/RevokedCertificate.java new file mode 100644 index 00000000..b3002736 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/RevokedCertificate.java @@ -0,0 +1,92 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.math.BigInteger; +import java.util.Date; +import java.util.Set; + +import java.security.cert.*; + +/** + * <p>Abstract class for a revoked certificate in a CRL (Certificate + * Revocation List). + * + * The ASN.1 definition for <em>revokedCertificates</em> is: + * <pre> + * revokedCertificates SEQUENCE OF SEQUENCE { + * userCertificate CertificateSerialNumber, + * revocationDate ChoiceOfTime, + * crlEntryExtensions Extensions OPTIONAL + * -- if present, must be v2 + * } OPTIONAL + *<p> + * CertificateSerialNumber ::= INTEGER + *<p> + * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension + *<p> + * Extension ::= SEQUENCE { + * extnId OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * extnValue OCTET STRING + * -- contains a DER encoding of a value + * -- of the type registered for use with + * -- the extnId object identifier value + * } + * </pre> + * + * @see X509CRL + * + * @author Hemma Prafullchandra + * @version 1.4 97/12/10 + */ + +public abstract class RevokedCertificate extends X509CRLEntry { +/* implements X509Extension { */ + + /** + * Gets the serial number for this RevokedCertificate, + * the <em>userCertificate</em>. + * + * @return the serial number. + */ + public abstract BigInteger getSerialNumber(); + + /** + * Gets the revocation date for this RevokedCertificate, + * the <em>revocationDate</em>. + * + * @return the revocation date. + */ + public abstract Date getRevocationDate(); + + /** + * Returns true if this revoked certificate entry has + * extensions. + * + * @return true if this entry has extensions, false otherwise. + */ + public abstract boolean hasExtensions(); + + /** + * Returns a string representation of this revoked certificate. + * + * @return a string representation of this revoked certificate. + */ + public abstract String toString(); +} diff --git a/pki/base/util/src/netscape/security/x509/SerialNumber.java b/pki/base/util/src/netscape/security/x509/SerialNumber.java new file mode 100644 index 00000000..344ecb7b --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/SerialNumber.java @@ -0,0 +1,121 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; + +import netscape.security.util.*; + +/** + * This class defines the SerialNumber class used by certificates. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.5 + */ +public class SerialNumber { + private BigInt serialNum; + + // Construct the class from the DerValue + private void construct(DerValue derVal) throws IOException { + serialNum = derVal.getInteger(); + if (derVal.data.available() != 0) { + throw new IOException("Excess SerialNumber data"); + } + } + + /** + * The default constructor for this class using BigInteger. + * + * @param num the BigInteger number used to create the serial number. + */ + public SerialNumber(BigInteger num) { + serialNum = new BigInt(num); + } + + public SerialNumber(BigInt num) { + serialNum = num; + } + + /** + * The default constructor for this class using int. + * + * @param num the BigInteger number used to create the serial number. + */ + public SerialNumber(int num) { + serialNum = new BigInt(num); + } + + /** + * Create the object, decoding the values from the passed DER stream. + * + * @param in the DerInputStream to read the SerialNumber from. + * @exception IOException on decoding errors. + */ + public SerialNumber(DerInputStream in) throws IOException { + DerValue derVal = in.getDerValue(); + construct(derVal); + } + + /** + * Create the object, decoding the values from the passed DerValue. + * + * @param val the DerValue to read the SerialNumber from. + * @exception IOException on decoding errors. + */ + public SerialNumber(DerValue val) throws IOException { + construct(val); + } + + /** + * Create the object, decoding the values from the passed stream. + * + * @param in the InputStream to read the SerialNumber from. + * @exception IOException on decoding errors. + */ + public SerialNumber(InputStream in) throws IOException { + DerValue derVal = new DerValue(in); + construct(derVal); + } + + /** + * Return the SerialNumber as user readable string. + */ + public String toString() { + return ("SerialNumber: [" + serialNum.toString() + "]"); + } + + /** + * Encode the SerialNumber in DER form to the stream. + * + * @param out the DerOutputStream to marshal the contents to. + * @exception IOException on errors. + */ + public void encode(DerOutputStream out) throws IOException { + out.putInteger(serialNum); + } + + /** + * Return the serial number. + */ + public BigInt getNumber() { + return (serialNum); + } +} diff --git a/pki/base/util/src/netscape/security/x509/SubjectAlternativeNameExtension.java b/pki/base/util/src/netscape/security/x509/SubjectAlternativeNameExtension.java new file mode 100644 index 00000000..c57bfcc9 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/SubjectAlternativeNameExtension.java @@ -0,0 +1,232 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.util.Enumeration; + +import netscape.security.util.*; + +/** + * This represents the Subject Alternative Name Extension. + * + * This extension, if present, allows the subject to specify multiple + * alternative names. + * + * <p>Extensions are represented as a sequence of the extension identifier + * (Object Identifier), a boolean flag stating whether the extension is to + * be treated as being critical and the extension value itself (this is again + * a DER encoding of the extension value). + * <p> + * The ASN.1 syntax for this is: + * <pre> + * SubjectAltName ::= GeneralNames + * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + * </pre> + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.9 + * @see Extension + * @see CertAttrSet + */ +public class SubjectAlternativeNameExtension extends Extension +implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = + "x509.info.extensions.SubjectAlternativeName"; + /** + * Attribute names. + */ + public static final String NAME = "SubjectAlternativeName"; + public static final String SUBJECT_NAME = "subject_name"; + + // private data members + GeneralNames names; + + // Encode this extension + private void encodeThis() throws IOException { + DerOutputStream os = new DerOutputStream(); + try { + names.encode(os); + } catch (GeneralNamesException e) { + throw new IOException("SubjectAlternativeName: " + e); + } + extensionValue = os.toByteArray(); + } + + /** + * Create a SubjectAlternativeNameExtension with the passed GeneralNames. + * + * @param names the GeneralNames for the subject. + * @exception IOException on error. + */ + public SubjectAlternativeNameExtension(boolean critical, GeneralNames names) + throws IOException { + this.names = names; + this.extensionId = PKIXExtensions.SubjectAlternativeName_Id; + this.critical = critical; + encodeThis(); + } + public SubjectAlternativeNameExtension(GeneralNames names) + throws IOException { + this.names = names; + this.extensionId = PKIXExtensions.SubjectAlternativeName_Id; + this.critical = false; + encodeThis(); + } + + /** + * Create a default SubjectAlternativeNameExtension. + */ + public SubjectAlternativeNameExtension() { + extensionId = PKIXExtensions.SubjectAlternativeName_Id; + critical = false; + names = new GeneralNames(); + } + + /** + * Create the extension from the passed DER encoded value. + * + * @param critical true if the extension is to be treated as critical. + * @param value Array of DER encoded bytes of the actual value. + * @exception IOException on error. + */ + public SubjectAlternativeNameExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.SubjectAlternativeName_Id; + this.critical = critical.booleanValue(); + + if (!(value instanceof byte[])) + throw new IOException("SubjectAlternativeName: " + + "Illegal argument type"); + + int len = Array.getLength(value); + byte[] extValue = new byte[len]; + System.arraycopy(value, 0, extValue, 0, len); + + this.extensionValue = extValue; + DerValue val = new DerValue(extValue); + try { + names = new GeneralNames(val); + } catch (GeneralNamesException e) { + throw new IOException("SubjectAlternativeName: " + e); + } + } + + /** + * Returns a printable representation of the SubjectAlternativeName. + */ + public String toString() { + if (names == null) return ""; + String s = super.toString() + "SubjectAlternativeName [\n" + + names.toString() + "]\n"; + return (s); + } + + /** + * Decode the extension from the InputStream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception IOException on decoding or validity errors. + */ + public void decode(InputStream in) throws IOException { + throw new IOException("Method not to be called directly."); + } + + /** + * Write the extension to the OutputStream. + * + * @param out the OutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (extensionValue == null) { + extensionId = PKIXExtensions.SubjectAlternativeName_Id; + //critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + clearValue(); + if (name.equalsIgnoreCase(SUBJECT_NAME)) { + if (!(obj instanceof GeneralNames)) { + throw new IOException("Attribute value should be of " + + "type GeneralNames."); + } + names = (GeneralNames)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:SubjectAlternativeName."); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(SUBJECT_NAME)) { + return (names); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:SubjectAlternativeName."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(SUBJECT_NAME)) { + names = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:SubjectAlternativeName."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements () { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(SUBJECT_NAME); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName () { + return (NAME); + } +} diff --git a/pki/base/util/src/netscape/security/x509/SubjectDirAttributesExtension.java b/pki/base/util/src/netscape/security/x509/SubjectDirAttributesExtension.java new file mode 100644 index 00000000..65f6fef4 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/SubjectDirAttributesExtension.java @@ -0,0 +1,280 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.util.Enumeration; +import java.util.Vector; + +import netscape.security.util.*; + +/** + * This class represents the Subject Directory Attributes Extension. + * + * <p>The subject directory attributes extension is not recommended as an + * essential part of this profile, but it may be used in local environments. + * This extension MUST be non-critical. + * + * <pre> + * The ASN.1 syntax for this extension is: + * + * SubjectDirectoryAttributes ::= SEQUENCE (1..MAX) OF Attribute + * + * Attribute ::= SEQUENCE { + * type AttributeType, + * value SET OF AttributeValue + * -- at least one value is required --} + * + * AttributeType ::= OBJECT IDENTIFIER + * + * AttributeValue ::= ANY + * + * </pre> + * + * @author Christine Ho + * @version 1.7 + * + * @see CertAttrSet + * @see Extension + */ +public class SubjectDirAttributesExtension extends Extension +implements CertAttrSet { + + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + //public static final String IDENT = "x509.info.extensions.SubjectDirectoryAttributes"; + public static final String IDENT = "Subject Directory Attributes"; + + /** + * Attribute names. + */ + public static final String NAME = "SubjectDirectoryAttributes"; + + // Private data members + private Vector attrList = new Vector(); + + // Encode this extension value + private void encodeThis() throws IOException { + DerOutputStream out = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + + //encoding the attributes + Enumeration attrs = attrList.elements(); + while (attrs.hasMoreElements()) { + Attribute attr = (Attribute) attrs.nextElement(); + attr.encode(tmp); + } + + out.write(DerValue.tag_SequenceOf, tmp); + this.extensionValue = out.toByteArray(); + } + + // Decode this extension value + private void decodeThis(DerValue derVal) throws IOException { + + if (derVal.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for "+ + "Subject Directory Attribute extension."); + } + + if (derVal.data.available() == 0) { + throw new IOException(NAME+" No data available in " + + "passed DER encoded value."); + } + + // Decode all the Attributes + while (derVal.data.available() != 0) { + DerValue encAttr = derVal.data.getDerValue(); + Attribute attr = new Attribute(encAttr); + attrList.addElement(attr); + } + } + + /** + * Default constructor for this object. + * + * @param derVal Der encoded value of this extension + */ + public SubjectDirAttributesExtension(DerValue derVal) throws IOException { + + this.extensionId = PKIXExtensions.SubjectDirectoryAttributes_Id; + this.critical = false; + decodeThis(derVal); + } + + /** + * Default constructor for this object. + * + * @param list Attribute object list + */ + public SubjectDirAttributesExtension(Attribute[] list) throws IOException { + + this.extensionId = PKIXExtensions.SubjectDirectoryAttributes_Id; + this.critical = false; + + if ((list == null)||(list.length==0)) { + throw new IOException("No data available in " + + "passed Attribute List."); + } + + // add the Attributes + for (int i = 0; i < list.length; i++) { + attrList.addElement(list[i]); + } + } + + /** + * Constructor from parsing extension + * + * @param list Attribute object list + */ + public SubjectDirAttributesExtension(Boolean crit, Object value) + throws IOException { + + this.extensionId = PKIXExtensions.SubjectDirectoryAttributes_Id; + this.critical = crit.booleanValue(); + + if (!(value instanceof byte[])) + throw new IOException(NAME+"Illegal argument type"); + int len = Array.getLength(value); + byte[] extValue = new byte[len]; + System.arraycopy(value, 0, extValue, 0, len); + + this.extensionValue = extValue; + decodeThis(new DerValue(extValue)); + } + + /** + * Constructor for this object. + * + * @param list Attribute object list + * @param critical The criticality + */ + public SubjectDirAttributesExtension(Attribute[] list, boolean critical) + throws IOException { + + this.extensionId = PKIXExtensions.SubjectDirectoryAttributes_Id; + this.critical = critical; + + if ((list == null)||(list.length==0)) { + throw new IOException("No data available in " + + "passed Attribute List."); + } + + // add the Attributes + for (int i = 0; i < list.length; i++) { + attrList.addElement(list[i]); + } + } + + /** + * Return user readable form of extension. + */ + public String toString() { + String s = super.toString() + "SubjectDirectoryAttributes:[\n"; + + Enumeration attrs = attrList.elements(); + while (attrs.hasMoreElements()) { + Attribute attr = (Attribute) attrs.nextElement(); + s += attr.toString(); + } + + return (s + "]\n"); + } + + /** + * Decode the extension from the InputStream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception IOException on decoding or validity errors. + */ + public void decode(InputStream in) throws IOException { + DerValue val = new DerValue(in); + decodeThis(val); + } + + /** + * Encode this extension value to the output stream. + * + * @param out the DerOutputStream to encode the extension to. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (extensionValue == null) { + this.extensionId = PKIXExtensions.SubjectDirectoryAttributes_Id; + this.critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:SubjectDirectoryAttributes."); + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:SubjectDirectoryAttributes."); + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:SubjectDirectoryAttributes."); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } + + /** + * Returns an enumeration of attributes in the extension. + */ + public Enumeration getAttributesList() { + if (attrList == null) + return null; + return attrList.elements(); + } +} diff --git a/pki/base/util/src/netscape/security/x509/SubjectKeyIdentifierExtension.java b/pki/base/util/src/netscape/security/x509/SubjectKeyIdentifierExtension.java new file mode 100644 index 00000000..dd3a61e2 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/SubjectKeyIdentifierExtension.java @@ -0,0 +1,213 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.util.Enumeration; + +import netscape.security.util.*; + +/** + * Represent the Subject Key Identifier Extension. + * + * This extension, if present, provides a means of identifying the particular + * public key used in an application. This extension by default is marked + * non-critical. + * + * <p>Extensions are addiitonal attributes which can be inserted in a X509 + * v3 certificate. For example a "Driving License Certificate" could have + * the driving license number as a extension. + * + * <p>Extensions are represented as a sequence of the extension identifier + * (Object Identifier), a boolean flag stating whether the extension is to + * be treated as being critical and the extension value itself (this is again + * a DER encoding of the extension value). + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.7 + * @see Extension + * @see CertAttrSet + */ +public class SubjectKeyIdentifierExtension extends Extension +implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = + "x509.info.extensions.SubjectKeyIdentifier"; + /** + * Attribute names. + */ + public static final String NAME = "SubjectKeyIdentifier"; + public static final String KEY_ID = "key_id"; + + // Private data member + private KeyIdentifier id; + + // Encode this extension value + private void encodeThis() throws IOException { + DerOutputStream os = new DerOutputStream(); + id.encode(os); + extensionValue = os.toByteArray(); + } + + /** + * Create a SubjectKeyIdentifierExtension with the passed octet string. + * The criticality is set to False. + * @param octetString the octet string identifying the key identifier. + */ + public SubjectKeyIdentifierExtension (boolean critical, byte[] octetString) + throws IOException { + id = new KeyIdentifier(octetString); + + this.extensionId = PKIXExtensions.SubjectKey_Id; + this.critical = critical; + encodeThis(); + } + public SubjectKeyIdentifierExtension (byte[] octetString) + throws IOException { + id = new KeyIdentifier(octetString); + + this.extensionId = PKIXExtensions.SubjectKey_Id; + this.critical = false; + encodeThis(); + } + + /** + * Create the extension from the passed DER encoded value. + * + * @param critical true if the extension is to be treated as critical. + * @param value Array of DER encoded bytes of the actual value. + * @exception IOException on error. + */ + public SubjectKeyIdentifierExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.SubjectKey_Id; + this.critical = critical.booleanValue(); + + int len = Array.getLength(value); + byte[] extValue = new byte[len]; + for (int i = 0; i < len; i++) { + extValue[i] = Array.getByte(value,i); + } + this.extensionValue = extValue; + DerValue val = new DerValue(extValue); + this.id = new KeyIdentifier(val); + } + + /** + * Returns a printable representation. + */ + public String toString() { + if (id == null) return ""; + String s = super.toString() + "SubjectKeyIdentifier [\n" + + id.toString() + "]\n"; + return (s); + } + + /** + * Write the extension to the OutputStream. + * + * @param out the OutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (extensionValue == null) { + extensionId = PKIXExtensions.SubjectKey_Id; + critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Decode the extension from the InputStream. + * + * @param in the InputStream to unmarshal the contents from. + * @exception IOException on decoding or validity errors. + */ + public void decode(InputStream in) throws IOException { + throw new IOException("Method not to be called directly."); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + clearValue(); + if (name.equalsIgnoreCase(KEY_ID)) { + if (!(obj instanceof KeyIdentifier)) { + throw new IOException("Attribute value should be of" + + " type KeyIdentifier."); + } + id = (KeyIdentifier)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:SubjectKeyIdentifierExtension."); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(KEY_ID)) { + return (id); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:SubjectKeyIdentifierExtension."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(KEY_ID)) { + id = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:SubjectKeyIdentifierExtension."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements () { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(KEY_ID); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName () { + return (NAME); + } +} diff --git a/pki/base/util/src/netscape/security/x509/URIName.java b/pki/base/util/src/netscape/security/x509/URIName.java new file mode 100644 index 00000000..3dc80489 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/URIName.java @@ -0,0 +1,80 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; + +import netscape.security.util.*; + +/** + * This class implements the URIName as required by the GeneralNames + * ASN.1 object. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.3 + * @see GeneralName + * @see GeneralNames + * @see GeneralNameInterface + */ +public class URIName implements GeneralNameInterface { + private String name; + + /** + * Create the URIName object from the passed encoded Der value. + * + * @param derValue the encoded DER URIName. + * @exception IOException on error. + */ + public URIName(DerValue derValue) throws IOException { + name = derValue.getIA5String(); + } + + /** + * Create the URIName object with the specified name. + * + * @param name the URIName. + */ + public URIName(String name) { + this.name = name; + } + + /** + * Return the type of the GeneralName. + */ + public int getType() { + return (GeneralNameInterface.NAME_URI); + } + + /** + * Encode the URI name into the DerOutputStream. + * + * @param out the DER stream to encode the URIName to. + * @exception IOException on encoding errors. + */ + public void encode(DerOutputStream out) throws IOException { + out.putIA5String(name); + } + + /** + * Convert the name into user readable string. + */ + public String toString() { + return ("URIName: " + name); + } +} diff --git a/pki/base/util/src/netscape/security/x509/UniqueIdentity.java b/pki/base/util/src/netscape/security/x509/UniqueIdentity.java new file mode 100644 index 00000000..4b33ad77 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/UniqueIdentity.java @@ -0,0 +1,109 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.math.BigInteger; + +import netscape.security.util.*; + +/** + * This class defines the UniqueIdentity class used by certificates. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.6 + */ +public class UniqueIdentity { + // Private data members + private BitArray id; + + /** + * The default constructor for this class. + * + * @param id the byte array containing the unique identifier. + */ + public UniqueIdentity(BitArray id) { + this.id = id; + } + + /** + * The default constructor for this class. + * + * @param id the byte array containing the unique identifier. + */ + public UniqueIdentity(byte[] id) { + this.id = new BitArray(id.length*8, id); + } + + /** + * Create the object, decoding the values from the passed DER stream. + * + * @param in the DerInputStream to read the UniqueIdentity from. + * @exception IOException on decoding errors. + */ + public UniqueIdentity(DerInputStream in) throws IOException { + DerValue derVal = in.getDerValue(); + id = derVal.getUnalignedBitString(true); + } + + /** + * Create the object, decoding the values from the passed DER stream. + * + * @param derVal the DerValue decoded from the stream. + * @param tag the tag the value is encoded under. + * @exception IOException on decoding errors. + */ + public UniqueIdentity(DerValue derVal) throws IOException { + id = derVal.getUnalignedBitString(true); + } + + /** + * Return the UniqueIdentity as a printable string. + */ + public String toString() { + return ("UniqueIdentity:" + id.toString() + "\n"); + } + + /** + * Encode the UniqueIdentity in DER form to the stream. + * + * @param out the DerOutputStream to marshal the contents to. + * @param tag enocode it under the following tag. + * @exception IOException on errors. + */ + public void encode(DerOutputStream out, byte tag) throws IOException { + byte[] bytes = id.toByteArray(); + int excessBits = bytes.length*8 - id.length(); + + out.write(tag); + out.putLength(bytes.length + 1); + + out.write(excessBits); + out.write(bytes); + } + + /** + * Return the unique id. + */ + public boolean[] getId() { + if (id == null) return null; + + return id.toBooleanArray(); + } +} diff --git a/pki/base/util/src/netscape/security/x509/UserNotice.java b/pki/base/util/src/netscape/security/x509/UserNotice.java new file mode 100644 index 00000000..01bef6dd --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/UserNotice.java @@ -0,0 +1,91 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import netscape.security.util.*; + + +/** + * Represent the UserNotice Qualifier. + * + * UserNotice ::= SEQUENCE { + * noticeRef NoticeReference OPTIONAL, + * explicitText DisplayText OPTIONAL + * } + * + * @author Thomas Kwan + */ +public class UserNotice extends Qualifier { + + private NoticeReference mNoticeReference = null; + private DisplayText mDisplayText = null; + + public UserNotice(NoticeReference ref, DisplayText text) { + mNoticeReference = ref; + mDisplayText = text; + } + + public UserNotice(DerValue val) throws IOException { + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for UserNotice"); + } + // case 0: no element + if (val.data.available() == 0) + return; + // case 1: 1 element + DerValue inSeq = val.data.getDerValue(); + if (inSeq.tag == DerValue.tag_Sequence) { + mNoticeReference = new NoticeReference(inSeq); + } else { + mDisplayText = new DisplayText(inSeq); + } + if (val.data.available() == 0) + return; + // case 2: 2 elements + mDisplayText = new DisplayText(val.data.getDerValue()); + } + + public NoticeReference getNoticeReference() { + return mNoticeReference; + } + + public DisplayText getDisplayText() { + return mDisplayText; + } + + /** + * Write the UserNotice to the DerOutputStream. + * + * @param out the DerOutputStream to write the object to. + * @exception IOException on errors. + */ + public void encode(DerOutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + + // OPTIONAL + if (mNoticeReference != null) { + mNoticeReference.encode(tmp); + } + // OPTIONAL + if (mDisplayText != null) { + mDisplayText.encode(tmp); + } + out.write(DerValue.tag_Sequence,tmp); + } +} diff --git a/pki/base/util/src/netscape/security/x509/X500Name.java b/pki/base/util/src/netscape/security/x509/X500Name.java new file mode 100644 index 00000000..f94b759f --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/X500Name.java @@ -0,0 +1,739 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.StringReader; +import java.io.IOException; +import java.io.StringBufferInputStream; +import java.security.Principal; +import java.util.Enumeration; +import java.util.Vector; + +import netscape.security.util.*; + + +/** + * X.500 names are used to identify entities, such as those which are + * identified by X.509 certificates. They are world-wide, hierarchical, + * and descriptive. Entities can be identified by attributes, and in + * some systems can be searched for according to those attributes. + * + * <P><em>This class exposes only partial X.500 name functionality. Most + * notably, it works best if Relative Distinguished Names only have one + * (unique) attribute each, and if only the most common attributes need + * to be visible to applications. This limitation, and others, will + * be lifted over time.</em> + * + * @author David Brownell + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.35 + * @see GeneralName + * @see GeneralNames + * @see GeneralNameInterface + * @see RDN + * @see AVA + * @see LdapDNStrConverter + */ + + +public class X500Name implements Principal, GeneralNameInterface { + /** + * Constructs a name from a Ldap DN string, such + * as &lb;CN=Dave, OU=JavaSoft, O=Sun Microsystems, C=US&rb;. The + * older "/C=US/O=Sun Microsystems, Inc/OU=JavaSoft/CN=Dave" syntax + * is not currently supported. (The former is RFC 1779 style.) + * + * @param ldapDNString a Ldap DN String e.g. as defined in RFC1779 + */ + public X500Name (String ldapDNString) + throws IOException + { + X500Name x500name; + + if(ldapDNString == null || ldapDNString.equals("")) + { + clear(); + return; + } + x500name = LdapDNStrConverter.getDefault().parseDN(ldapDNString); + names = x500name.getNames(); + } + + /** + * Constructs a X500Name from a Ldap DN String using the specified + * LdapDNStrConverter. Also use the input tags. + * @see LdapDNStrConverter + * + * @param ldapDNString a Ldap DN String e.g. as defined in RFC1779. + * @param ldapDNStrConverter A LdapDNStrConverter + */ + public X500Name (String ldapDNString,LdapDNStrConverter ldapDNStrConverter,byte[] tags) + throws IOException + { + + if(ldapDNString == null || ldapDNString.equals("")) + { + clear(); + return; + } + X500Name x500name; + x500name = ldapDNStrConverter.parseDN(ldapDNString,tags); + names = x500name.getNames(); + + } + + public X500Name (String ldapDNString, byte[] tags) + throws IOException + { + if(ldapDNString == null || ldapDNString.equals("")) + { + clear(); + return; + } + X500Name x500name; + x500name = LdapDNStrConverter.getDefault().parseDN(ldapDNString, tags); + names = x500name.getNames(); + } + + /** + * Constructs a X500Name from a Ldap DN String using the specified + * LdapDNStrConverter. + * @see LdapDNStrConverter + * + * @param ldapDNString a Ldap DN String e.g. as defined in RFC1779. + * @param ldapDNStrConverter A LdapDNStrConverter + */ + public X500Name (String ldapDNString, + LdapDNStrConverter ldapDNStrConverter) + throws IOException + { + if(ldapDNString == null || ldapDNString.equals("")) + { + clear(); + return; + } + X500Name x500name; + x500name = ldapDNStrConverter.parseDN(ldapDNString); + names = x500name.getNames(); + } + + /** + * Constructs a X500Name from fields common in enterprise application + * environments. + * + * @param commonName common name of a person, e.g. "Vivette Davis" + * @param organizationUnit small organization name, e.g. "Purchasing" + * @param organizationName large organization name, e.g. "Onizuka, Inc." + * @param country two letter country code, e.g. "CH" + */ + public X500Name ( + String commonName, + String organizationUnit, + String organizationName, + String country + ) throws IOException + { + DirStrConverter dirStrConverter = new DirStrConverter(); + PrintableConverter printableConverter = new PrintableConverter(); + DerValue val; + AVA[] assertion = new AVA[1]; // array is cloned in constructors. + int i = 4; + + names = new RDN [i]; + /* + * NOTE: it's only on output that little-endian + * ordering is used. + */ + assertion[0] = new AVA(commonName_oid, + dirStrConverter.getValue(commonName)); + names [--i] = new RDN (assertion); + + assertion[0] = new AVA(orgUnitName_oid, + dirStrConverter.getValue(organizationUnit)); + names [--i] = new RDN (assertion); + + assertion[0] = new AVA(orgName_oid, + dirStrConverter.getValue(organizationName)); + names [--i] = new RDN (assertion); + + assertion[0] = new AVA(countryName_oid, + printableConverter.getValue(country)); + names [--i] = new RDN (assertion); + } + + /** + * Constructs a X500Name from fields common in Internet application + * environments. + * + * @param commonName common name of a person, e.g. "Vivette Davis" + * @param organizationUnit small organization name, e.g. "Purchasing" + * @param organizationName large organization name, e.g. "Onizuka, Inc." + * @param localityName locality (city) name, e.g. "Palo Alto" + * @param stateName state name, e.g. "California" + * @param country two letter country code, e.g. "CH" + */ + public X500Name ( + String commonName, + String organizationUnit, + String organizationName, + String localityName, + String stateName, + String country + ) throws IOException + { + DirStrConverter dirStrConverter = new DirStrConverter(); + PrintableConverter printableConverter = new PrintableConverter(); + DerValue val; + AVA[] assertion = new AVA[1]; // array is cloned in constructors. + int i = 6; + + names = new RDN [i]; + /* + * NOTE: it's only on output that little-endian + * ordering is used. + */ + assertion[0] = new AVA(commonName_oid, + dirStrConverter.getValue(commonName)); + names [--i] = new RDN (assertion); + + assertion[0] = new AVA(orgUnitName_oid, + dirStrConverter.getValue(organizationUnit)); + names [--i] = new RDN (assertion); + + assertion[0] = new AVA(orgName_oid, + dirStrConverter.getValue(organizationName)); + names [--i] = new RDN (assertion); + + assertion[0] = new AVA(localityName_oid, + dirStrConverter.getValue(localityName)); + names [--i] = new RDN (assertion); + + assertion[0] = new AVA(stateName_oid, + dirStrConverter.getValue(stateName)); + names [--i] = new RDN (assertion); + + assertion[0] = new AVA(countryName_oid, + printableConverter.getValue(country)); + names [--i] = new RDN (assertion); + } + + + /** + * Constructs a name from an ASN.1 encoded value. The encoding + * of the name in the stream uses DER (a BER/1 subset). + * + * @param value a DER-encoded value holding an X.500 name. + */ + public X500Name(DerValue value) throws IOException { + + this(value.toDerInputStream()); + } + + /** + * Constructs a name from an ASN.1 encoded input stream. The encoding + * of the name in the stream uses DER (a BER/1 subset). + * + * @param in DER-encoded data holding an X.500 name. + */ + public X500Name (DerInputStream in) + throws IOException + { + parseDER (in); + } + + /** + * Constructs a name from an ASN.1 encoded byte array. + * + * @param name DER-encoded byte array holding an X.500 name. + */ + public X500Name (byte[] name) + throws IOException + { + DerInputStream in = new DerInputStream(name); + parseDER (in); + + } + + /** + * Constructs a X500Name from array of RDN. The RDNs are expected to + * be in big endian order i.e. most significant first. + * @param rdns an array of RDN. + */ + public X500Name (RDN[] rdns) + throws IOException + { + names = (RDN[])rdns.clone(); + } + + /** + * convenience method. + * @param rdns a vector of rdns. + */ + public X500Name (Vector rdnVector) + throws IOException + { + int size = rdnVector.size(); + names = new RDN[size]; + for (int i = 0; i < size; i++) { + names[i] = (RDN)rdnVector.elementAt(i); + } + } + + /** + * Compares this name with another, for equality. + * + * @return true iff the names are identical. + */ + synchronized public boolean equals (X500Name other) + { + int i; + + if (this == other) + return true; + + if (names.length != other.names.length) + return false; + for (i = 0; i < names.length; i++) { + if (!names [i].equals (other.names [i])) + return false; + } + return true; + } + + /** + * Sets private data to a null state + */ + + private void clear() + { + dn = ""; + names = null; + + } + + /** + * Returns the name component as a Java string, regardless of its + * encoding restrictions. + */ + private String getString (DerValue attribute) throws IOException + { + String value = attribute.getAsString (); + + if (value == null) + throw new IOException ("not a DER string encoding, " + + attribute.tag); + else + return value; + } + + /** + * Return type of GeneralName. + */ + public int getType() { + return (GeneralNameInterface.NAME_DIRECTORY); + } + + /** + * Returns a "Country" name component. If more than one + * such attribute exists, the topmost one is returned. + * + * @return "C=" component of the name, if any. + */ + public String getCountry () throws IOException + { + DerValue attr = findAttribute (countryName_oid); + + return getString (attr); + } + + + /** + * Returns an "Organization" name component. If more than + * one such attribute exists, the topmost one is returned. + * + * @return "O=" component of the name, if any. + */ + public String getOrganization () throws IOException + { + DerValue attr = findAttribute (orgName_oid); + + return getString (attr); + } + + + /** + * Returns an "Organizational Unit" name component. If more + * than one such attribute exists, the topmost one is returned. + * + * @return "OU=" component of the name, if any. + */ + public String getOrganizationalUnit () throws IOException + { + DerValue attr = findAttribute (orgUnitName_oid); + + return getString (attr); + } + + + /** + * Returns a "Common Name" component. If more than one such + * attribute exists, the topmost one is returned. + * + * @return "CN=" component of the name, if any. + */ + public String getCommonName () throws IOException + { + DerValue attr = findAttribute (commonName_oid); + + return getString (attr); + } + + + /** + * Returns a "Locality" name component. If more than one + * such component exists, the topmost one is returned. + * + * @return "L=" component of the name, if any. + */ + public String getLocality () throws IOException + { + DerValue attr = findAttribute (localityName_oid); + + return getString (attr); + } + + + /** + * Returns a "State" name component. If more than one + * such component exists, the topmost one is returned. + * + * @return "S=" component of the name, if any. + */ + public String getState () throws IOException + { + DerValue attr = findAttribute (stateName_oid); + + return getString (attr); + } + + /** + * Returns a "Email" name component. If more than one + * such component exists, the topmost one is returned. + * + * @return "E=" component of the name, if any. + */ + public String getEmail() throws IOException + { + DerValue attr = findAttribute (email_oid); + if (attr == null) + return null; + return getString (attr); + } + + /** + * Returns a Ldap DN String from the X500Name using the global default + * LdapDNStrConverter + * @see LdapDNStrConverter + * @return Ldap DN string of this X500Name using the default converter. + */ + public String toLdapDNString() + throws IOException + { + if (dn == null) + generateDN(LdapDNStrConverter.getDefault()); + return dn; + } + + /** + * Returns a Ldap DN String from the X500Name + * using the specified LdapDNStrconverter. + * For example, RFC1779String converter can be passed to convert the + * DN to RFC1779 string syntax. + * @see LdapDNStrConverter + * @param ldapDNStrConverter a LdapDNStrConverter + * @return Ldap DN string of the X500Name + */ + public String toLdapDNString(LdapDNStrConverter ldapDNStrConverter) + throws IOException + { + + if (dn == null) + generateDN(ldapDNStrConverter); + return dn; + } + + /** + * Returns a Ldap DN string, using the global default LdapDNStrConverter + * or null if an error occurs in the conversion. + */ + public String toString() + { + String s; + if(names == null) + { + s = ""; + return s; + } + try { + s = toLdapDNString(); + } + catch (IOException e) { + return null; + } + return s; + } + + /** + * Returns the value of toString(). This call is needed to + * implement the java.security.Principal interface. + */ + public String getName () { return toString (); } + + + private String dn; // RFC 1779 style DN, or null + private RDN names[]; // RDNs + + /** + * Find the first instance of this attribute in a "top down" + * search of all the attributes in the name. + */ + private DerValue findAttribute (ObjectIdentifier attribute) + { + int i; + DerValue retval = null; + + for (i = 0; i < names.length; i++) { + retval = names [i].findAttribute (attribute); + if (retval != null) + break; + } + return retval; + } + + /** + * Returns an enumerator of RDNs in the X500Name. + * @return enumeration of rdns in this X500Name. + */ + public Enumeration getRDNs() + { + return new RDNEnumerator(); + } + + /** + * Returns an array of RDN in the X500Name. + * @return array of RDN in this X500name. + */ + public RDN[] getNames() + { + return (RDN[])names.clone(); + } + + /** + * Returns the number of RDNs in the X500Name. + * @return number of RDNs in this X500Name. + */ + public int getNamesLength() + { + return names.length; + } + + /****************************************************************/ + + private void parseDER (DerInputStream in) throws IOException + { + // + // X.500 names are a "SEQUENCE OF" RDNs, which means one or + // more and order matters. We scan them in order, which + // conventionally is big-endian. + // + DerValue nameseq [] = in.getSequence (5); + int i; + + if(nameseq.length != 0) + { + names = new RDN [nameseq.length]; + } + else + { + clear(); + } + + + for (i = 0; i < nameseq.length; i++) + names [i] = new RDN (nameseq [i]); + } + + /** + * Encodes the name in DER-encoded form. + * + * @param out where to put the DER-encoded X.500 name + */ + public void encode (DerOutputStream out) throws IOException + { + DerOutputStream tmp = new DerOutputStream (); + int i; + + int len = 0; + if(names == null) + { + len = 0; + } + else + { + len = names.length; + + } + + for (i = 0; i < len; i++) + names [i].encode (tmp); + + out.write (DerValue.tag_Sequence, tmp); + } + + /** + * Gets the name in DER-encoded form. + * + * @return the DER encoded byte array of this name, + * null if no names are present. + */ + public byte[] getEncoded() throws IOException { + + DerOutputStream out = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + + int len = 0; + + if (names == null) + { + len = 0; + } + else + { + len = names.length; + } + + for (int i = 0; i < len; i++) + names[i].encode(tmp); + + out.write(DerValue.tag_Sequence, tmp); + return out.toByteArray(); + } + + /* + * Dump the printable form of a distinguished name. Each relative + * name is separated from the next by a ",", and assertions in the + * relative names have "label=value" syntax. + * + * Uses RFC 1779 syntax (i.e. little-endian, comma separators) + * + */ + private void generateDN(LdapDNStrConverter ldapDNStrConverter) + throws IOException + { + if(names == null) + return ; + + dn = ldapDNStrConverter.encodeDN(this); + } + + private class RDNEnumerator implements Enumeration + { + private int index; + + public RDNEnumerator() { index = 0; } + + public boolean hasMoreElements() + { + return (index < names.length); + } + + public Object nextElement() + { + if (index >= names.length) + return null; + return names[index++]; + } + } + + /****************************************************************/ + + /* + * Maybe return a preallocated OID, to reduce storage costs + * and speed recognition of common X.500 attributes. + */ + static ObjectIdentifier intern (ObjectIdentifier oid) + throws IOException + { + return X500NameAttrMap.getDefault().getOid(oid); + } + + /* + * Selected OIDs from X.520 + */ + + /** OID for the "CN=" attribute, denoting a person's common name. */ + public static final ObjectIdentifier + commonName_oid = X500NameAttrMap.getDefault().getOid("CN"); + + /** OID for the "C=" attribute, denoting a country. */ + public static final ObjectIdentifier + countryName_oid = X500NameAttrMap.getDefault().getOid("C"); + + /** OID for the "L=" attribute, denoting a locality (such as a city) */ + public static final ObjectIdentifier + localityName_oid = X500NameAttrMap.getDefault().getOid("L"); + + /** OID for the "O=" attribute, denoting an organization name */ + public static final ObjectIdentifier + orgName_oid = X500NameAttrMap.getDefault().getOid("O"); + + /** OID for the "OU=" attribute, denoting an organizational unit name */ + public static final ObjectIdentifier + orgUnitName_oid = X500NameAttrMap.getDefault().getOid("OU"); + + /** OID for the "S=" attribute, denoting a state (such as Delaware) */ + public static final ObjectIdentifier + stateName_oid = X500NameAttrMap.getDefault().getOid("ST"); + + /** OID for the "STREET=" attribute, denoting a street address. */ + public static final ObjectIdentifier + streetAddress_oid = X500NameAttrMap.getDefault().getOid("STREET"); + + /** OID for the "T=" attribute, denoting a person's title. */ + public static final ObjectIdentifier + title_oid = X500NameAttrMap.getDefault().getOid("TITLE"); + + /** OID for the "E=" attribute, denoting a person's email address. */ + public static final ObjectIdentifier + email_oid = X500NameAttrMap.getDefault().getOid("E"); + + /* + * OIDs from other sources which show up in X.500 names we + * expect to deal with often + */ + + private static final int ipAddress_data [] = // SKIP + { 1, 3, 6, 1, 4, 1, 42, 2, 11, 2, 1 }; + + /** OID for "IP=" IP address attributes, used with SKIP. */ + public static final ObjectIdentifier + ipAddress_oid = new ObjectIdentifier (ipAddress_data); +} + + diff --git a/pki/base/util/src/netscape/security/x509/X500NameAttrMap.java b/pki/base/util/src/netscape/security/x509/X500NameAttrMap.java new file mode 100644 index 00000000..8edbae35 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/X500NameAttrMap.java @@ -0,0 +1,390 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.*; +import java.util.*; + +import netscape.security.util.ObjectIdentifier; + +/** + * Maps an attribute name in an X500 AVA to its OID and a + * converter for the attribute type. The converter converts from a string to + * its DER encoded attribute value. * For example, "CN" maps to its OID of + * 2.5.4.3 and the Directory String Converter. The Directory String + * Converter converts from a string to a DerValue with tag Printable, T.61 or + * UniversalString. + * + * @author Lily Hsiao, Slava Galperin at Netscape Communications, Inc. + * + */ + +public class X500NameAttrMap +{ + // + // public constructors. + // + + /** + * Construct a X500NameAttrMap. + */ + public X500NameAttrMap() + { + } + + // + // public get methods. + // + + /** + * Get the attribute name (keyword) of the specified OID. + * + * @param oid An ObjectIdentifier + * + * @return An attribute name (keyword string) for the OID. + */ + public String getName(ObjectIdentifier oid) + { + // XXX assert oid != null + return (String)oid2Name.get(oid); + } + + /** + * Get the ObjectIdentifier of the attribute name. + * + * @param name An attribute name (string of ascii characters) + * + * @return An ObjectIdentifier for the attribute. + */ + public ObjectIdentifier getOid(String name) + { + // XXX assert name != null + return (ObjectIdentifier)name2OID.get(name.toUpperCase()); + } + + /** + * Get the Attribute Value Converter for the specified attribute name. + * + * @param name An attribute name + * + * @return An attribute value converter for the attribute name + */ + public AVAValueConverter getValueConverter(String name) + { + ObjectIdentifier oid = (ObjectIdentifier) + name2OID.get(name.toUpperCase()); + if (oid == null) return null; + return (AVAValueConverter)oid2ValueConverter.get(oid); + } + + /** + * Get the Attribute Value Converter for the specified ObjectIdentifier. + * + * @param oid An ObjectIdentifier + * + * @return An AVAValueConverter for the OID. + */ + public AVAValueConverter getValueConverter(ObjectIdentifier oid) + { + return (AVAValueConverter)oid2ValueConverter.get(oid); + } + + /** + * Get an Enumeration of all attribute names in this map. + * + * @return An Enumeration of all attribute names. + */ + public Enumeration getAllNames() + { + return name2OID.keys(); + } + + /** + * Get an Enumeration of all ObjectIdentifiers in this map. + * + * @return An Enumeration of all OIDs in this map. + */ + public Enumeration getAllOIDs() + { + return oid2Name.keys(); + } + + /** + * Get the ObjectIdentifier object in the map for the specified OID. + * + * @param oid An ObjectIdentifier. + * @return The ObjectIdentifier object in this map for the OID. + */ + public ObjectIdentifier getOid(ObjectIdentifier oid) + { + String name = (String)oid2Name.get(oid); + if (name == null) + return null; + return (ObjectIdentifier)name2OID.get(name); + } + + + // + // public add methods. + // + + /** + * Adds a attribute name, ObjectIdentifier, AVAValueConverter entry + * to the map. + * + * @param name An attribute name (string of ascii chars) + * @param oid The ObjectIdentifier for the attribute. + * @param valueConverter An AVAValueConverter object for converting + * an value for this attribute from a string to + * a DerValue and vice versa. + */ + public void addNameOID(String name, ObjectIdentifier oid, + AVAValueConverter valueConverter) + { + // normalize name for case insensitive compare. + ObjectIdentifier theOid; + Class expValueConverter; + + theOid = (ObjectIdentifier)name2OID.get(name); + if (theOid != null) { + expValueConverter = oid2ValueConverter.get(theOid).getClass(); + if (!theOid.equals(oid) || + expValueConverter != valueConverter.getClass()) { + throw new IllegalArgumentException( + "Another keyword-oid-valueConverter triple already " + + "exists in the X500NameAttrMap "); + } + return; + } + name2OID.put(name.toUpperCase(), oid); + oid2Name.put(oid, name.toUpperCase()); + oid2ValueConverter.put(oid, valueConverter); + } + + // + // public static methods. + // + + /** + * Get the global default X500NameAttrMap. + * + * @return The global default X500NameAttrMap. + */ + public static X500NameAttrMap getDefault() + { + return defMap; + } + + /** + * Get the global default X500NamAttrMap using the DirStrConverter. + * + * @return The global default X500NameAttrMap using the DirStrConverter. + */ + + public static X500NameAttrMap getDirDefault() + { + return defDirMap; + + } + + /** + * Set the global default X500NameAttrMap. + * + * @param newDefault The new default X500NameAttrMap. + */ + public static void setDefault(X500NameAttrMap newDefault) + { + // XXX assert newDef != null + defMap = newDefault; + } + + // + // private variables + // + + Hashtable name2OID = new Hashtable(); + Hashtable oid2Name = new Hashtable(); + Hashtable oid2ValueConverter = new Hashtable(); + + // + // global defaults. + // + + private static X500NameAttrMap defMap; + + private static X500NameAttrMap defDirMap; + + /* + * Create the default maps on initialization. + */ + static { + defMap = new X500NameAttrMap(); + AVAValueConverter directoryStr = new DirStrConverter(), + ia5Str = new IA5StringConverter(); + defMap.addNameOID("CN", + new ObjectIdentifier("2.5.4.3"), + directoryStr); + defMap.addNameOID("OU", + new ObjectIdentifier("2.5.4.11"), + directoryStr); + defMap.addNameOID("O", + new ObjectIdentifier("2.5.4.10"), + directoryStr); + // serialNumber added for CEP support + defMap.addNameOID("SERIALNUMBER", + new ObjectIdentifier("2.5.4.5"), + new PrintableConverter()); + defMap.addNameOID("C", + new ObjectIdentifier("2.5.4.6"), + new PrintableConverter()); + defMap.addNameOID("L", + new ObjectIdentifier("2.5.4.7"), + directoryStr); + defMap.addNameOID("ST", + new ObjectIdentifier("2.5.4.8"), + directoryStr); + defMap.addNameOID("STREET", + new ObjectIdentifier("2.5.4.9"), + directoryStr); + defMap.addNameOID("TITLE", + new ObjectIdentifier("2.5.4.12"), + directoryStr); + // RFC 1274 UserId, rfc822MailBox + defMap.addNameOID("UID", + new ObjectIdentifier("0.9.2342.19200300.100.1.1"), + directoryStr); + defMap.addNameOID("MAIL", + new ObjectIdentifier("0.9.2342.19200300.100.1.3"), + ia5Str); + // PKCS9 e-mail address + defMap.addNameOID("E", + new ObjectIdentifier("1.2.840.113549.1.9.1"), + ia5Str); + + // DC definition from draft-ietf-asid-ldap-domains-02.txt + defMap.addNameOID("DC", + new ObjectIdentifier("0.9.2342.19200300.100.1.25"), + ia5Str); + + // more defined in RFC2459 used in Subject Directory Attr extension + defMap.addNameOID("SN", // surname + new ObjectIdentifier("2.5.4.4"), + directoryStr); + defMap.addNameOID("GIVENNAME", + new ObjectIdentifier("2.5.4.42"), + directoryStr); + defMap.addNameOID("INITIALS", + new ObjectIdentifier("2.5.4.43"), + directoryStr); + defMap.addNameOID("GENERATIONQUALIFIER", + new ObjectIdentifier("2.5.4.44"), + directoryStr); + defMap.addNameOID("DNQUALIFIER", + new ObjectIdentifier("2.5.4.46"), + directoryStr); + + // these two added mainly for CEP support + // PKCS9 unstructured name + defMap.addNameOID("UNSTRUCTUREDNAME", + new ObjectIdentifier("1.2.840.113549.1.9.2"), + ia5Str); + // PKCS9 unstructured address + defMap.addNameOID("UNSTRUCTUREDADDRESS", + new ObjectIdentifier("1.2.840.113549.1.9.8"), + new PrintableConverter()); + }; + + static { + defDirMap = new X500NameAttrMap(); + AVAValueConverter directoryStr = new DirStrConverter(); + + defDirMap.addNameOID("CN", + new ObjectIdentifier("2.5.4.3"), + directoryStr); + defDirMap.addNameOID("OU", + new ObjectIdentifier("2.5.4.11"), + directoryStr); + defDirMap.addNameOID("O", + new ObjectIdentifier("2.5.4.10"), + directoryStr); + // serialNumber added for CEP support + defDirMap.addNameOID("SERIALNUMBER", + new ObjectIdentifier("2.5.4.5"), + directoryStr); + defDirMap.addNameOID("C", + new ObjectIdentifier("2.5.4.6"), + directoryStr); + defDirMap.addNameOID("L", + new ObjectIdentifier("2.5.4.7"), + directoryStr); + defDirMap.addNameOID("ST", + new ObjectIdentifier("2.5.4.8"), + directoryStr); + defDirMap.addNameOID("STREET", + new ObjectIdentifier("2.5.4.9"), + directoryStr); + defDirMap.addNameOID("TITLE", + new ObjectIdentifier("2.5.4.12"), + directoryStr); + // RFC 1274 UserId, rfc822MailBox + defDirMap.addNameOID("UID", + new ObjectIdentifier("0.9.2342.19200300.100.1.1"), + directoryStr); + defDirMap.addNameOID("MAIL", + new ObjectIdentifier("0.9.2342.19200300.100.1.3"), + directoryStr); + // PKCS9 e-mail address + defDirMap.addNameOID("E", + new ObjectIdentifier("1.2.840.113549.1.9.1"), + directoryStr); + + // DC definition from draft-ietf-asid-ldap-domains-02.txt + defDirMap.addNameOID("DC", + new ObjectIdentifier("0.9.2342.19200300.100.1.25"), + directoryStr); + + // more defined in RFC2459 used in Subject Directory Attr extension + defDirMap.addNameOID("SN", // surname + new ObjectIdentifier("2.5.4.4"), + directoryStr); + defDirMap.addNameOID("GIVENNAME", + new ObjectIdentifier("2.5.4.42"), + directoryStr); + defDirMap.addNameOID("INITIALS", + new ObjectIdentifier("2.5.4.43"), + directoryStr); + defDirMap.addNameOID("GENERATIONQUALIFIER", + new ObjectIdentifier("2.5.4.44"), + directoryStr); + defDirMap.addNameOID("DNQUALIFIER", + new ObjectIdentifier("2.5.4.46"), + directoryStr); + + // these two added mainly for CEP support + // PKCS9 unstructured name + defDirMap.addNameOID("UNSTRUCTUREDNAME", + new ObjectIdentifier("1.2.840.113549.1.9.2"), + directoryStr); + // PKCS9 unstructured address + defDirMap.addNameOID("UNSTRUCTUREDADDRESS", + new ObjectIdentifier("1.2.840.113549.1.9.8"), + directoryStr); + }; + +} + diff --git a/pki/base/util/src/netscape/security/x509/X500Signer.java b/pki/base/util/src/netscape/security/x509/X500Signer.java new file mode 100644 index 00000000..cd13c335 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/X500Signer.java @@ -0,0 +1,111 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.security.Signature; +import java.security.SignatureException; +import java.security.Signer; +import java.security.NoSuchAlgorithmException; + +/** + * This class provides a binding between a Signature object and an + * authenticated X.500 name (from an X.509 certificate chain), which + * is needed in many public key signing applications. + * + * <P>The name of the signer is important, both because knowing it is the + * whole point of the signature, and because the associated X.509 certificate + * is always used to verify the signature. + * + * <P><em>The X.509 certificate chain is temporarily not associated with + * the signer, but this omission will be resolved.</em> + * + * @version 1.18 + * + * @author David Brownell + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public final class X500Signer extends Signer +{ + /** + * Called for each chunk of the data being signed. That + * is, you can present the data in many chunks, so that + * it doesn't need to be in a single sequential buffer. + * + * @param buf buffer holding the next chunk of the data to be signed + * @param offset starting point of to-be-signed data + * @param len how many bytes of data are to be signed + * @exception SignatureException on errors. + */ + public void update(byte buf[], int offset, int len) + throws SignatureException { + sig.update (buf, offset, len); + } + + /** + * Produces the signature for the data processed by update(). + * + * @exception SignatureException on errors. + */ + public byte[] sign() throws SignatureException { + return sig.sign(); + } + + /** + * Returns the algorithm used to sign. + */ + public AlgorithmId getAlgorithmId() { + return algid; + } + + /** + * Returns the name of the signing agent. + */ + public X500Name getSigner() { + return agent; + } + + /* + * Constructs a binding between a signature and an X500 name + * from an X.509 certificate. + */ + // package private ----hmmmmm ????? + public X500Signer(Signature sig, X500Name agent) { + if (sig == null || agent == null) + throw new IllegalArgumentException ("null parameter"); + + this.sig = sig; + this.agent = agent; + + try { + this.algid = AlgorithmId.getAlgorithmId(sig.getAlgorithm()); + String alg = sig.getAlgorithm(); + if( alg.equals("DSA") ) { + alg = "SHA1withDSA"; + } + this.algid = AlgorithmId.getAlgorithmId(alg); + + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("internal error! " + e.getMessage()); + } + } + + private Signature sig; + private X500Name agent; // XXX should be X509CertChain + private AlgorithmId algid; +} diff --git a/pki/base/util/src/netscape/security/x509/X509AttributeName.java b/pki/base/util/src/netscape/security/x509/X509AttributeName.java new file mode 100644 index 00000000..4f62f61a --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/X509AttributeName.java @@ -0,0 +1,64 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +/** + * This class is used to parse attribute names like "x509.info.extensions". + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.4 + */ +public class X509AttributeName { + // Public members + private static final char SEPARATOR = '.'; + + // Private data members + private String prefix = null; + private String suffix = null; + + /** + * Default constructor for the class. Name is of the form + * "x509.info.extensions". + * + * @param name the attribute name. + */ + public X509AttributeName(String name) { + int i = name.indexOf(SEPARATOR); + if (i == (-1)) { + prefix = name; + } else { + prefix = name.substring(0, i); + suffix = name.substring(i + 1); + } + } + + /** + * Return the prefix of the name. + */ + public String getPrefix() { + return (prefix); + } + + /** + * Return the suffix of the name. + */ + public String getSuffix() { + return (suffix); + } +} diff --git a/pki/base/util/src/netscape/security/x509/X509CRLImpl.java b/pki/base/util/src/netscape/security/x509/X509CRLImpl.java new file mode 100755 index 00000000..f6a365f9 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/X509CRLImpl.java @@ -0,0 +1,1014 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.security.Principal; +import java.security.PublicKey; +import java.security.PrivateKey; +import java.security.Security; +import java.security.Signature; +import java.security.NoSuchAlgorithmException; +import java.security.InvalidKeyException; +import java.security.NoSuchProviderException; +import java.security.SignatureException; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.security.cert.X509CRL; +import java.security.cert.X509CRLEntry; +import java.security.cert.CRLException; +import java.security.cert.CertificateException; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Set; + +import netscape.security.util.*; + +/** + * <p> + * An implmentation for X509 CRL (Certificate Revocation List). + * <p> + * The X.509 v2 CRL format is described below in ASN.1: + * <pre> + * </pre> + * <p> + * CertificateList ::= SEQUENCE { + * tbsCertList TBSCertList, + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING } + * <p> + * A good description and profiling is provided in the IETF PKIX WG + * draft, Part I: X.509 Certificate and CRL Profile, + * <draft-ietf-pkix-ipki-part1-06.txt>. + * <p> + * The ASN.1 definition of <code>tbsCertList</code> is: + * <pre> + * TBSCertList ::= SEQUENCE { + * version Version OPTIONAL, + * -- if present, must be v2 + * signature AlgorithmIdentifier, + * issuer Name, + * thisUpdate ChoiceOfTime, + * nextUpdate ChoiceOfTime OPTIONAL, + * revokedCertificates SEQUENCE OF SEQUENCE { + * userCertificate CertificateSerialNumber, + * revocationDate ChoiceOfTime, + * crlEntryExtensions Extensions OPTIONAL + * -- if present, must be v2 + * } OPTIONAL, + * crlExtensions [0] EXPLICIT Extensions OPTIONAL + * -- if present, must be v2 + * } + * </pre> + * + * @author Hemma Prafullchandra + * @version 1.8 + * @see X509CRL + */ +public class X509CRLImpl extends X509CRL { + + // CRL data, and its envelope + private byte[] signedCRL; // DER encoded crl + private byte[] signature; // raw signature bits + private byte[] tbsCertList; // DER encoded "to-be-signed" CRL + private AlgorithmId sigAlgId; // sig alg in CRL + + // crl information + private int version; + private AlgorithmId infoSigAlgId; // sig alg in "to-be-signed" crl + private X500Name issuer; + private Date thisUpdate = null; + private Date nextUpdate = null; +// private static final Hashtable revokedCerts = new Hashtable(); + private Hashtable revokedCerts = new Hashtable(); +// private static CRLExtensions extensions = null; + private CRLExtensions extensions = null; + private boolean entriesIncluded = true; + private final static boolean isExplicit = true; + + private boolean readOnly = false; + + /** + * Not to be used. As it would lead to cases of uninitialized + * CRL objects. + */ + private X509CRLImpl() { } + + /** + * Unmarshals an X.509 CRL from its encoded form, parsing the encoded + * bytes. This form of constructor is used by agents which + * need to examine and use CRL contents. Note that the buffer + * must include only one CRL, and no "garbage" may be left at + * the end. + * + * @param crlData the encoded bytes, with no trailing padding. + * @exception CRLException on parsing errors. + * @exception X509ExtensionException on extension handling errors. + */ + public X509CRLImpl(byte[] crlData) + throws CRLException, X509ExtensionException { + try { + DerValue in = new DerValue(crlData); + + parse(in); + signedCRL = crlData; + } catch (IOException e) { + throw new CRLException("Parsing error: " + e.getMessage()); + } + } + + public X509CRLImpl(byte[] crlData, boolean includeEntries) + throws CRLException, X509ExtensionException { + try { + entriesIncluded = includeEntries; + DerValue in = new DerValue(crlData); + + parse(in, includeEntries); + signedCRL = crlData; + } catch (IOException e) { + throw new CRLException("Parsing error: " + e.getMessage()); + } + } + + /** + * Unmarshals an X.509 CRL from an input stream. Only one CRL + * is expected at the end of the input stream. + * + * @param inStrm an input stream holding at least one CRL + * @exception CRLException on parsing errors. + * @exception X509ExtensionException on extension handling errors. + */ + public X509CRLImpl(InputStream inStrm) + throws CRLException, X509ExtensionException { + try { + DerValue val = new DerValue(inStrm); + + parse(val); + signedCRL = val.toByteArray(); + } catch (IOException e) { + throw new CRLException("Parsing error: " + e.getMessage()); + } + } + + /** + * Initial CRL constructor, no revoked certs, and no extensions. + * + * @param issuer the name of the CA issuing this CRL. + * @param thisUpdate the Date of this issue. + * @param nextUpdate the Date of the next CRL. + */ + public X509CRLImpl(X500Name issuer, Date thisDate, Date nextDate) { + this.issuer = issuer; + this.thisUpdate = thisDate; + this.nextUpdate = nextDate; + } + + /** + * CRL constructor, revoked certs, no extensions. + * + * @param issuer the name of the CA issuing this CRL. + * @param thisUpdate the Date of this issue. + * @param nextUpdate the Date of the next CRL. + * @param badCerts the array of revoked certificates. + * + * @exception CRLException on parsing/construction errors. + * @exception X509ExtensionException on extension handling errors. + */ + public X509CRLImpl(X500Name issuer, Date thisDate, Date nextDate, + RevokedCertificate[] badCerts) + throws CRLException, X509ExtensionException { + this.issuer = issuer; + this.thisUpdate = thisDate; + this.nextUpdate = nextDate; + if (badCerts != null) { + for (int i = 0; i < badCerts.length; i++) + this.revokedCerts.put(badCerts[i].getSerialNumber(), + badCerts[i]); + } + } + + /** + * CRL constructor, revoked certs and extensions. + * + * @param issuer the name of the CA issuing this CRL. + * @param thisUpdate the Date of this issue. + * @param nextUpdate the Date of the next CRL. + * @param badCerts the array of revoked certificates. + * @param crlExts the CRL extensions. + * + * @exception CRLException on parsing/construction errors. + * @exception X509ExtensionException on extension handling errors. + */ + public X509CRLImpl(X500Name issuer, Date thisDate, Date nextDate, + RevokedCertificate[] badCerts, CRLExtensions crlExts) + throws CRLException, X509ExtensionException { + this.issuer = issuer; + this.thisUpdate = thisDate; + this.nextUpdate = nextDate; + if (badCerts != null) { + for (int i = 0; i < badCerts.length; i++) { + if (badCerts[i] != null) { + this.revokedCerts.put(badCerts[i].getSerialNumber(), + badCerts[i]); + if (badCerts[i].hasExtensions()) + this.version = 1; + } + } + } + if (crlExts != null) { + this.extensions = crlExts; + this.version = 1; + } + } + + + /** + * CRL constructor, revoked certs and extensions. + * This will be used by code that constructs CRL and uses + * encodeInfo() in order to sign it using external means + * (other than sign() method) + * + * @param issuer the name of the CA issuing this CRL. + * @param sigAlg signing algorithm id + * @param thisUpdate the Date of this issue. + * @param nextUpdate the Date of the next CRL. + * @param badCerts the array of revoked certificates. + * @param crlExts the CRL extensions. + */ + public X509CRLImpl(X500Name issuer, AlgorithmId algId, Date thisDate, Date nextDate, + RevokedCertificate[] badCerts, CRLExtensions crlExts) + throws CRLException, X509ExtensionException { + this(issuer,thisDate,nextDate,badCerts,crlExts); + infoSigAlgId = algId; + } + + + /** + * CRL constructor, revoked certs and extensions. + * + * @param issuer the name of the CA issuing this CRL. + * @param sigAlg signing algorithm id + * @param thisUpdate the Date of this issue. + * @param nextUpdate the Date of the next CRL. + * @param badCerts the hashtable of revoked certificates. + * @param crlExts the CRL extensions. + * + * @exception CRLException on parsing/construction errors. + * @exception X509ExtensionException on extension handling errors. + */ + public X509CRLImpl(X500Name issuer, AlgorithmId algId, + Date thisDate, Date nextDate, + Hashtable badCerts, CRLExtensions crlExts) + throws CRLException, X509ExtensionException { + this.issuer = issuer; + this.thisUpdate = thisDate; + this.nextUpdate = nextDate; + this.revokedCerts = badCerts; + if (crlExts != null) { + this.extensions = crlExts; + this.version = 1; + } + infoSigAlgId = algId; + } + + + /** + * Returns the ASN.1 DER encoded form of this CRL. + * + * @exception CRLException if an encoding error occurs. + */ + public byte[] getEncoded() throws CRLException { + if (signedCRL == null) + throw new CRLException("Null CRL to encode"); + byte[] dup = new byte[signedCRL.length]; + System.arraycopy(signedCRL, 0, dup, 0, dup.length); + return dup; + } + + public boolean hasUnsupportedCriticalExtension() { + // XXX NOT IMPLEMENTED + return true; + } + + /** + * Encodes the "to-be-signed" CRL to the OutputStream. + * + * @param out the OutputStream to write to. + * @exception CRLException on encoding errors. + * @exception X509ExtensionException on extension encoding errors. + */ + public void encodeInfo(OutputStream out) + throws CRLException, X509ExtensionException { + try { + DerOutputStream tmp = new DerOutputStream(); + DerOutputStream rCerts = new DerOutputStream(); + DerOutputStream seq = new DerOutputStream(); + + if (version != 0) // v2 crl encode version + tmp.putInteger(new BigInt(version)); + infoSigAlgId.encode(tmp); + issuer.encode(tmp); + + // from 2050 should encode GeneralizedTime + tmp.putUTCTime(thisUpdate); + + if (nextUpdate != null) + tmp.putUTCTime(nextUpdate); + + if (! revokedCerts.isEmpty()) { + for (Enumeration e = revokedCerts.elements(); + e.hasMoreElements();) + ((RevokedCertImpl)e.nextElement()).encode(rCerts); + tmp.write(DerValue.tag_Sequence, rCerts); + } + + if (extensions != null) + extensions.encode(tmp, isExplicit); + + seq.write(DerValue.tag_Sequence, tmp); + + tbsCertList = seq.toByteArray(); + out.write(tbsCertList); + } catch (IOException e) { + throw new CRLException("Encoding error: " + e.getMessage()); + } + } + + /** + * Verifies that this CRL was signed using the + * private key that corresponds to the specified public key. + * + * @param key the PublicKey used to carry out the verification. + * + * @exception NoSuchAlgorithmException on unsupported signature + * algorithms. + * @exception InvalidKeyException on incorrect key. + * @exception NoSuchProviderException if there's no default provider. + * @exception SignatureException on signature errors. + * @exception CRLException on encoding errors. + */ + public void verify(PublicKey key) + throws CRLException, NoSuchAlgorithmException, InvalidKeyException, + NoSuchProviderException, SignatureException { + verify(key, null); + } + + /** + * Verifies that this CRL was signed using the + * private key that corresponds to the specified public key, + * and that the signature verification was computed by + * the given provider. + * + * @param key the PublicKey used to carry out the verification. + * @param sigProvider the name of the signature provider. + * + * @exception NoSuchAlgorithmException on unsupported signature + * algorithms. + * @exception InvalidKeyException on incorrect key. + * @exception NoSuchProviderException on incorrect provider. + * @exception SignatureException on signature errors. + * @exception CRLException on encoding errors. + */ + public void verify(PublicKey key, String sigProvider) + throws CRLException, NoSuchAlgorithmException, InvalidKeyException, + NoSuchProviderException, SignatureException { + if (signedCRL == null) { + throw new CRLException("Uninitialized CRL"); + } + Signature sigVerf = null; + + String sigAlg = sigAlgId.getName(); + if (sigProvider.equals("Mozilla-JSS")) { + if (sigAlg.equals("MD5withRSA")) { + sigAlg = "MD5/RSA"; + } else if (sigAlg.equals("MD2withRSA")) { + sigAlg = "MD2/RSA"; + } else if (sigAlg.equals("SHA1withRSA")) { + sigAlg = "SHA1/RSA"; + } else if (sigAlg.equals("SHA1withDSA")) { + sigAlg = "SHA1/DSA"; + } + } + sigVerf = Signature.getInstance(sigAlg, sigProvider); + sigVerf.initVerify(key); + + if (tbsCertList == null) + throw new CRLException("Uninitialized CRL"); + + sigVerf.update(tbsCertList, 0, tbsCertList.length); + + if (!sigVerf.verify(signature)) { + throw new CRLException("Signature does not match."); + } + } + + /** + * Encodes an X.509 CRL, and signs it using the key + * passed. + * + * @param key the private key used for signing. + * @param algorithm the name of the signature algorithm used. + * + * @exception NoSuchAlgorithmException on unsupported signature + * algorithms. + * @exception InvalidKeyException on incorrect key. + * @exception NoSuchProviderException on incorrect provider. + * @exception SignatureException on signature errors. + * @exception CRLException if any mandatory data was omitted. + * @exception X509ExtensionException on any extension errors. + */ + public void sign(PrivateKey key, String algorithm) + throws CRLException, NoSuchAlgorithmException, InvalidKeyException, + NoSuchProviderException, SignatureException, X509ExtensionException { + sign(key, algorithm, null); + } + + /** + * Encodes an X.509 CRL, and signs it using the key + * passed. + * + * @param key the private key used for signing. + * @param algorithm the name of the signature algorithm used. + * @param provider the name of the provider. + * + * @exception NoSuchAlgorithmException on unsupported signature + * algorithms. + * @exception InvalidKeyException on incorrect key. + * @exception NoSuchProviderException on incorrect provider. + * @exception SignatureException on signature errors. + * @exception CRLException if any mandatory data was omitted. + * @exception X509ExtensionException on any extension errors. + */ + public void sign(PrivateKey key, String algorithm, String provider) + throws CRLException, NoSuchAlgorithmException, InvalidKeyException, + NoSuchProviderException, SignatureException, X509ExtensionException { + try { + if (readOnly) + throw new CRLException("cannot over-write existing CRL"); + Signature sigEngine = null; + if (provider == null) + sigEngine = Signature.getInstance(algorithm); + else + sigEngine = Signature.getInstance(algorithm, provider); + + sigEngine.initSign(key); + + // in case the name is reset + sigAlgId = AlgorithmId.get(sigEngine.getAlgorithm()); + infoSigAlgId = sigAlgId; + + DerOutputStream out = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + + // encode crl info + encodeInfo(tmp); + + // encode algorithm identifier + sigAlgId.encode(tmp); + + // Create and encode the signature itself. + sigEngine.update(tbsCertList, 0, tbsCertList.length); + signature = sigEngine.sign(); + tmp.putBitString(signature); + + // Wrap the signed data in a SEQUENCE { data, algorithm, sig } + out.write(DerValue.tag_Sequence, tmp); + signedCRL = out.toByteArray(); + readOnly = true; + + } catch (IOException e) { + throw new CRLException("Error while encoding data: " + + e.getMessage()); + } + } + + /** + * Returns a printable string of this CRL. + * + * @return value of this CRL in a printable form. + */ + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("X.509 CRL v" + (version+1) + "\n"); + sb.append("Signature Algorithm: " + sigAlgId.toString() + + ", OID=" + (sigAlgId.getOID()).toString() + "\n"); + sb.append("Issuer: " + issuer.toString() + "\n"); + sb.append("\nThis Update: " + thisUpdate.toString() + "\n"); + if (nextUpdate != null) + sb.append("Next Update: " + nextUpdate.toString() + "\n"); + if (revokedCerts.isEmpty()) + sb.append("\nNO certificates have been revoked\n"); + else { + sb.append("\nRevoked Certificates:\n"); + for (Enumeration e = revokedCerts.elements(); + e.hasMoreElements();) + sb.append(((RevokedCertificate)e.nextElement()).toString()); + } + if (extensions != null) { + for (int i = 0; i < extensions.size(); i++) { + sb.append("\nCRL Extension[" + i + "]: " + + ((Extension)(extensions.elementAt(i))).toString()); + } + } + netscape.security.util.PrettyPrintFormat pp = + new netscape.security.util.PrettyPrintFormat(" ", 20); + String signaturebits = pp.toHexString(signature); + sb.append("\nSignature:\n" + signaturebits); + + return sb.toString(); + } + + /** + * Checks whether the given serial number is on this CRL. + * + * @param serialNumber the number to check for. + * @return true if the given serial number is on this CRL, + * false otherwise. + */ + public boolean isRevoked(BigInteger serialNumber) { + if (revokedCerts == null || revokedCerts.isEmpty()) + return false; + return revokedCerts.containsKey(serialNumber); + } + + public boolean isRevoked(Certificate cert) { + if (cert == null) + return false; + if (cert instanceof X509Certificate) { + return isRevoked(((X509Certificate)cert).getSerialNumber()); + } else { + return false; + } + } + + /** + * Gets the version number from the CRL. + * The ASN.1 definition for this is: + * <pre> + * Version ::= INTEGER { v1(0), v2(1), v3(2) } + * -- v3 does not apply to CRLs but appears for consistency + * -- with definition of Version for certs + * </pre> + * @return the version number. + */ + public int getVersion() { + return version; + } + + /** + * Gets the issuer distinguished name from this CRL. + * The issuer name identifies the entity who has signed (and + * issued the CRL). The issuer name field contains an + * X.500 distinguished name (DN). + * The ASN.1 definition for this is: + * <pre> + * issuer Name + * + * Name ::= CHOICE { RDNSequence } + * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + * RelativeDistinguishedName ::= + * SET OF AttributeValueAssertion + * + * AttributeValueAssertion ::= SEQUENCE { + * AttributeType, + * AttributeValue } + * AttributeType ::= OBJECT IDENTIFIER + * AttributeValue ::= ANY + * </pre> + * The Name describes a hierarchical name composed of attributes, + * such as country name, and corresponding values, such as US. + * The type of the component AttributeValue is determined by the + * AttributeType; in general it will be a directoryString. + * A directoryString is usually one of PrintableString, + * TeletexString or UniversalString. + * @return the issuer name. + */ + public Principal getIssuerDN() { + return (Principal)issuer; + } + + /** + * Gets the thisUpdate date from the CRL. + * The ASN.1 definition for this is: + * + * @return the thisUpdate date from the CRL. + */ + public Date getThisUpdate() { + return (new Date(thisUpdate.getTime())); + } + + /** + * Gets the nextUpdate date from the CRL. + * + * @return the nextUpdate date from the CRL, or null if + * not present. + */ + public Date getNextUpdate() { + if (nextUpdate == null) + return null; + return (new Date(nextUpdate.getTime())); + } + + /** + * Get the revoked certificate from the CRL by the serial + * number provided. + * + * @return the revoked certificate or null if there is + * no entry in the CRL marked with the provided serial number. + * @see RevokedCertificate + */ + public X509CRLEntry getRevokedCertificate(BigInteger serialNumber) { + if (revokedCerts == null || revokedCerts.isEmpty()) + return null; + RevokedCertificate badCert = + (RevokedCertificate)revokedCerts.get(serialNumber); + return badCert; + } + + /** + * Gets all the revoked certificates from the CRL. + * A Set of RevokedCertificate. + * + * @return all the revoked certificates or null if there are + * none. + * @see RevokedCertificate + */ + public Set getRevokedCertificates() { + if (revokedCerts == null || revokedCerts.isEmpty()) + return null; + else { + ArraySet certSet = new ArraySet(); + Collection badCerts = revokedCerts.values(); + Object[] objs = badCerts.toArray(); + for (int i = 0; i < objs.length; i++) + certSet.add(objs[i]); + return certSet; + } + } + + public Hashtable getListOfRevokedCertificates() { + if (revokedCerts == null) + return null; + else + return (Hashtable)revokedCerts.clone(); + } + + public int getNumberOfRevokedCertificates() { + if (revokedCerts == null) + return -1; + else + return revokedCerts.size(); + } + + /** + * Gets the DER encoded CRL information, the + * <code>tbsCertList</code> from this CRL. + * This can be used to verify the signature independently. + * + * @return the DER encoded CRL information. + * @exception CRLException on parsing errors. + * @exception X509ExtensionException on extension parsing errors. + */ + public byte[] getTBSCertList() + throws CRLException { + if (tbsCertList == null) + throw new CRLException("Uninitialized CRL"); + byte[] dup = new byte[tbsCertList.length]; + System.arraycopy(tbsCertList, 0, dup, 0, dup.length); + return dup; + } + + /** + * Gets the raw Signature bits from the CRL. + * + * @return the signature. + */ + public byte[] getSignature() { + if (signature == null) + return null; + byte[] dup = new byte[signature.length]; + System.arraycopy(signature, 0, dup, 0, dup.length); + return dup; + } + + /** + * Gets the signature algorithm name for the CRL + * signature algorithm. For example, the string "SHA1withDSA". + * The ASN.1 definition for this is: + * <pre> + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL } + * -- contains a value of the type + * -- registered for use with the + * -- algorithm object identifier value + * </pre> + * + * @return the signature algorithm name. + */ + public String getSigAlgName() { + if (sigAlgId == null) + return null; + return sigAlgId.getName(); + } + + /** + * Gets the signature algorithm OID string from the CRL. + * An OID is represented by a set of positive whole number separated + * by ".", that means,<br> + * <positive whole number>.<positive whole number>.<...> + * For example, the string "1.2.840.10040.4.3" identifies the SHA-1 + * with DSA signature algorithm, as per the PKIX part I. + * + * @return the signature algorithm oid string. + */ + public String getSigAlgOID() { + if (sigAlgId == null) + return null; + ObjectIdentifier oid = sigAlgId.getOID(); + return oid.toString(); + } + + /** + * Gets the DER encoded signature algorithm parameters from this + * CRL's signature algorithm. In most cases, the signature + * algorithm parameters are null, the parameters are usually + * supplied with the Public Key. + * + * @return the DER encoded signature algorithm parameters, or + * null if no parameters are present. + */ + public byte[] getSigAlgParams() { + if (sigAlgId == null) + return null; + try { + return sigAlgId.getEncodedParams(); + } catch (IOException e) { + return null; + } + } + + /** + * Gets a Set of the extension(s) marked CRITICAL in the + * CRL by OID strings. + * + * @return a set of the extension oid strings in the + * CRL that are marked critical. + */ + public Set getCriticalExtensionOIDs() { + if (extensions == null) + return null; + ArraySet extSet = new ArraySet(); + Extension ex; + for (Enumeration e = extensions.getElements(); + e.hasMoreElements();) { + ex = (Extension)e.nextElement(); + if (ex.isCritical()) + extSet.add(((ObjectIdentifier)ex.getExtensionId()).toString()); + } + return extSet; + } + + /** + * Gets a Set of the extension(s) marked NON-CRITICAL in the + * CRL by OID strings. + * + * @return a set of the extension oid strings in the + * CRL that are NOT marked critical. + */ + public Set getNonCriticalExtensionOIDs() { + if (extensions == null) + return null; + ArraySet extSet = new ArraySet(); + Extension ex; + for (Enumeration e = extensions.getElements(); + e.hasMoreElements();) { + ex = (Extension)e.nextElement(); + if ( ! ex.isCritical()) + extSet.add(((ObjectIdentifier)ex.getExtensionId()).toString()); + } + return extSet; + } + + /** + * Gets the DER encoded OCTET string for the extension value + * (<code>extnValue</code>) identified by the passed in oid String. + * The <code>oid</code> string is + * represented by a set of positive whole number separated + * by ".", that means,<br> + * <positive whole number>.<positive whole number>.<...> + * + * @param oid the Object Identifier value for the extension. + * @return the der encoded octet string of the extension value. + */ + public byte[] getExtensionValue(String oid) { + if (extensions == null) + return null; + try { + String extAlias = OIDMap.getName(new ObjectIdentifier(oid)); + Extension crlExt = null; + + if (extAlias == null) { // may be unknown + ObjectIdentifier findOID = new ObjectIdentifier(oid); + Extension ex = null; + ObjectIdentifier inCertOID; + for (Enumeration e=extensions.getElements(); + e.hasMoreElements();) { + ex = (Extension)e.nextElement(); + inCertOID = ex.getExtensionId(); + if (inCertOID.equals(findOID)) { + crlExt = ex; + break; + } + } + } else + crlExt = extensions.get(extAlias); + if (crlExt == null) + return null; + byte[] extData = crlExt.getExtensionValue(); + if (extData == null) + return null; + DerOutputStream out = new DerOutputStream(); + out.putOctetString(extData); + return out.toByteArray(); + } catch (Exception e) { + return null; + } + } + + public BigInteger getCRLNumber() { + try { + CRLExtensions exts = getExtensions(); + if (exts == null) + return null; + Enumeration e = exts.getElements(); + while (e.hasMoreElements()) { + Extension ext = (Extension)e.nextElement(); + if (ext instanceof CRLNumberExtension) { + CRLNumberExtension numExt = (CRLNumberExtension)ext; + return (BigInteger)numExt.get(CRLNumberExtension.NUMBER); + } + } + } catch (Exception e) { + } + return null; + } + + /** + * Returns extensions for this impl. + * + * @param extn CRLExtensions + */ + public CRLExtensions getExtensions() { + return extensions; + } + + public boolean areEntriesIncluded() { + return entriesIncluded; + } + + + /*********************************************************************/ + /* + * Parses an X.509 CRL, should be used only by constructors. + */ + private void parse(DerValue val) + throws CRLException, IOException, X509ExtensionException { + parse(val, true); + } + + private void parse(DerValue val, boolean includeEntries) + throws CRLException, IOException, X509ExtensionException { + // check if can over write the certificate + if (readOnly) + throw new CRLException("cannot over-write existing CRL"); + + readOnly = true; + DerValue seq[] = new DerValue[3]; + + seq[0] = val.data.getDerValue(); + seq[1] = val.data.getDerValue(); + seq[2] = val.data.getDerValue(); + + if (val.data.available() != 0) + throw new CRLException("signed overrun, bytes = " + + val.data.available()); + + if (seq[0].tag != DerValue.tag_Sequence) + throw new CRLException("signed CRL fields invalid"); + + sigAlgId = AlgorithmId.parse(seq[1]); + signature = seq[2].getBitString(); + + if (seq[1].data.available() != 0) + throw new CRLException("AlgorithmId field overrun"); + + if (seq[2].data.available() != 0) + throw new CRLException("Signature field overrun"); + + // the tbsCertsList + tbsCertList = seq[0].toByteArray(); + + // parse the information + DerInputStream derStrm = seq[0].data; + DerValue tmp; + byte nextByte; + + // version (optional if v1) + version = 0; // by default, version = v1 == 0 + nextByte = (byte)derStrm.peekByte(); + if (nextByte == DerValue.tag_Integer) { + version = derStrm.getInteger().toInt(); + if (version != 1) // i.e. v2 + throw new CRLException("Invalid version"); + } + tmp = derStrm.getDerValue(); + // signature + { + AlgorithmId tmpId = AlgorithmId.parse(tmp); + if (! tmpId.equals(sigAlgId)) + throw new CRLException("Signature algorithm mismatch"); + + infoSigAlgId = tmpId; + } + // issuer + issuer = new X500Name(derStrm); + + // thisUpdate + // check if UTCTime encoded or GeneralizedTime + + nextByte = (byte)derStrm.peekByte(); + if (nextByte == DerValue.tag_UtcTime) { + thisUpdate = derStrm.getUTCTime(); + } else if (nextByte == DerValue.tag_GeneralizedTime) { + thisUpdate = derStrm.getGeneralizedTime(); + } else { + throw new CRLException("Invalid encoding for thisUpdate" + + " (tag=" + nextByte + ")"); + } + + if (derStrm.available() == 0) + return; // done parsing no more optional fields present + + // nextUpdate (optional) + nextByte = (byte)derStrm.peekByte(); + if (nextByte == DerValue.tag_UtcTime) { + nextUpdate = derStrm.getUTCTime(); + } else if (nextByte == DerValue.tag_GeneralizedTime) { + nextUpdate = derStrm.getGeneralizedTime(); + } // else it is not present + + if (derStrm.available() == 0) + return; // done parsing no more optional fields present + + // revokedCertificates (optional) + nextByte = (byte)derStrm.peekByte(); + if (includeEntries && (nextByte == DerValue.tag_SequenceOf) + && (! ((nextByte & 0x0c0) == 0x080))) { + DerValue[] badCerts = derStrm.getSequence(4); + for (int i = 0; i < badCerts.length; i++) { + RevokedCertImpl entry = new RevokedCertImpl(badCerts[i]); + if (entry.hasExtensions() && (version == 0)) + throw new CRLException("Invalid encoding, extensions" + + " not supported in CRL v1 entries."); + + + revokedCerts.put(entry.getSerialNumber(), + (RevokedCertificate)entry); + } + } + + if (derStrm.available() == 0) + return; // done parsing no extensions + + // crlExtensions (optional) + tmp = derStrm.getDerValue(); + if (tmp.isConstructed() && tmp.isContextSpecific((byte)0)) { + if (version == 0) + throw new CRLException("Invalid encoding, extensions not" + + " supported in CRL v1."); + extensions = new CRLExtensions(tmp.data); + } + } +} diff --git a/pki/base/util/src/netscape/security/x509/X509Cert.java b/pki/base/util/src/netscape/security/x509/X509Cert.java new file mode 100644 index 00000000..1f546bc3 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/X509Cert.java @@ -0,0 +1,877 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.security.*; +import java.util.Date; +import java.util.Enumeration; + +import netscape.security.util.*; // DER + +/** + * @author David Brownell + * @version 1.5 + * + * @see CertAndKeyGen + * @deprecated Use the new X509Certificate class. + * This class is only restored for backwards compatibility. + */ +public +class X509Cert implements Certificate, Serializable +{ + + /* The algorithm id */ + protected AlgorithmId algid; + + /** + * Construct a uninitialized X509 Cert on which <a href="#decode"> + * decode</a> must later be called (or which may be deserialized). + */ + // XXX deprecated, delete this + public X509Cert () { } + + + /** + * Unmarshals a certificate from its encoded form, parsing the + * encoded bytes. This form of constructor is used by agents which + * need to examine and use certificate contents. That is, this is + * one of the more commonly used constructors. Note that the buffer + * must include only a certificate, and no "garbage" may be left at + * the end. If you need to ignore data at the end of a certificate, + * use another constructor. + * + * @param cert the encoded bytes, with no terminatu (CONSUMED) + * @exception IOException when the certificate is improperly encoded. + */ + public X509Cert ( + byte cert [] + ) throws IOException + { + DerValue in = new DerValue (cert); + + parse (in); + if (in.data.available () != 0) + throw new CertParseError ("garbage at end"); + signedCert = cert; + } + + + /** + * Unmarshals a certificate from its encoded form, parsing the + * encoded bytes. This form of constructor is used by agents which + * need to examine and use certificate contents. That is, this is + * one of the most commonly used constructors. + * + * @param buf the buffer holding the encoded bytes + * @param offset the offset in the buffer where the bytes begin + * @param len how many bytes of certificate exist + * + * @exception IOException when the certificate is improperly encoded. + */ + public X509Cert ( + byte buf [], + int offset, + int len + ) throws IOException + { + DerValue in = new DerValue (buf, offset, len); + + parse (in); + if (in.data.available () != 0) + throw new CertParseError ("garbage at end"); + signedCert = new byte [len]; + System.arraycopy (buf, offset, signedCert, 0, len); + } + + + /** + * Unmarshal a certificate from its encoded form, parsing a DER value. + * This form of constructor is used by agents which need to examine + * and use certificate contents. + * + * @param derVal the der value containing the encoded cert. + * @exception IOException when the certificate is improperly encoded. + */ + public X509Cert (DerValue derVal) throws IOException + { + parse (derVal); + if (derVal.data.available () != 0) + throw new CertParseError ("garbage at end"); + signedCert = derVal.toByteArray (); + } + + + /** + * Partially constructs a certificate from descriptive parameters. + * This constructor may be used by Certificate Authority (CA) code, + * which later <a href="#signAndEncode">signs and encodes</a> the + * certificate. Also, self-signed certificates serve as CA certificates, + * and are sometimes used as certificate requests. + * + * <P>Until the certificate has been signed and encoded, some of + * the mandatory fields in the certificate will not be available + * via accessor functions: the serial number, issuer name and signing + * algorithm, and of course the signed certificate. The fields passed + * to this constructor are available, and must be non-null. + * + * <P>Note that the public key being signed is generally independent of + * the signature algorithm being used. So for example Diffie-Hellman + * keys (which do not support signatures) can be placed in X.509 + * certificates when some other signature algorithm (e.g. DSS/DSA, + * or one of the RSA based algorithms) is used. + * + * @see CertAndKeyGen + * + * @param subjectName the X.500 distinguished name being certified + * @param subjectPublicKey the public key being certified. This + * must be an "X509Key" implementing the "PublicKey" interface. + * @param notBefore the first time the certificate is valid + * @param notAfter the last time the certificate is valid + * + * @exception CertException if the public key is inappropriate + */ + public X509Cert ( + X500Name subjectName, + X509Key subjectPublicKey, + Date notBefore, + Date notAfter + ) throws CertException + { + subject = subjectName; + + if (!(subjectPublicKey instanceof PublicKey)) + throw new CertException (CertException.err_INVALID_PUBLIC_KEY, + "Doesn't implement PublicKey interface"); + + /* + * The X509 cert API requires X509 keys, else things break. + */ + pubkey = subjectPublicKey; + notbefore = notBefore; + notafter = notAfter; + version = 0; + } + + + /** + * Decode an X.509 certificate from an input stream. + * + * @param in an input stream holding at least one certificate + * @exception IOException when the certificate is improperly encoded. + */ + public void decode (InputStream in) throws IOException + { + DerValue val = new DerValue (in); + + parse (val); + if (val.data.available () != 0) + throw new CertParseError ("garbage at end"); + signedCert = val.toByteArray (); + } + + + /** + * Appends the certificate to an output stream. + * + * @param out an input stream to which the certificate is appended. + * @exception IOException when appending fails. + */ + public void encode (OutputStream out) throws IOException + { out.write (getSignedCert ()); } + + + /** + * Compares two certificates. This is false if the + * certificates are not both X.509 certs, otherwise it + * compares them as binary data. + * + * @param other the object being compared with this one + * @return true iff the certificates are equivalent + */ + public boolean equals (Object other) + { + if (other instanceof X509Cert) + return equals ((X509Cert) other); + else + return false; + } + + + /** + * Compares two certificates, returning false if any data + * differs between the two. + * + * @param other the object being compared with this one + * @return true iff the certificates are equivalent + */ + public boolean equals (X509Cert src) + { + if (this == src) + return true; + if (signedCert == null || src.signedCert == null) + return false; + if (signedCert.length != src.signedCert.length) + return false; + for (int i = 0; i < signedCert.length; i++) + if (signedCert [i] != src.signedCert [i]) + return false; + return true; + } + + + /** Returns the "X.509" format identifier. */ + public String getFormat () // for Certificate + { return "X.509"; } + + + /** Returns <a href="#getIssuerName">getIssuerName</a> */ + public Principal getGuarantor () // for Certificate + { return getIssuerName (); } + + + /** Returns <a href="#getSubjectName">getSubjectName</a> */ + public Principal getPrincipal () + { return getSubjectName (); } + + + /** + * Throws an exception if the certificate is invalid because it is + * now outside of the certificate's validity period, or because it + * was not signed using the verification key provided. Successfully + * verifying a certificate does <em>not</em> indicate that one should + * trust the entity which it represents. + * + * <P><em>Note that since this class represents only a single X.509 + * certificate, it cannot know anything about the certificate chain + * which is used to provide the verification key and to establish trust. + * Other code must manage and use those cert chains. + * + * <P>For now, you must walk the cert chain being used to verify any + * given cert. Start at the root, which is a self-signed certificate; + * verify it using the key inside the certificate. Then use that to + * verify the next certificate in the chain, issued by that CA. In + * this manner, verify each certificate until you reach the particular + * certificate you wish to verify. You should not use a certificate + * if any of the verification operations for its certificate chain + * were unsuccessful. + * </em> + * + * @param issuerPublicKey the public key of the issuing CA + * @exception CertException when the certificate is not valid. + */ + public void verify (PublicKey issuerPublicKey) + throws CertException + { + Date now = new Date (); + + if (now.before (notbefore)) + throw new CertException (CertException.verf_INVALID_NOTBEFORE); + if (now.after (notafter)) + throw new CertException (CertException.verf_INVALID_EXPIRED); + if (signedCert == null) + throw new CertException (CertException.verf_INVALID_SIG, + "?? certificate is not signed yet ??"); + + // + // Verify the signature ... + // + String algName = null; + + try { + Signature sigVerf = null; + + algName = issuerSigAlg.getName(); + sigVerf = Signature.getInstance(algName); + sigVerf.initVerify (issuerPublicKey); + sigVerf.update (rawCert, 0, rawCert.length); + + if (!sigVerf.verify (signature)) { + throw new CertException (CertException.verf_INVALID_SIG, + "Signature ... by <" + issuer + "> for <" + subject + ">"); + } + + // Gag -- too many catch clauses, let most through. + + } catch (NoSuchAlgorithmException e) { + throw new CertException (CertException.verf_INVALID_SIG, + "Unsupported signature algorithm (" + algName + ")"); + + } catch (InvalidKeyException e) { + // e.printStackTrace(); + throw new CertException (CertException.err_INVALID_PUBLIC_KEY, + "Algorithm (" + algName + ") rejected public key"); + + } catch (SignatureException e) { + throw new CertException (CertException.verf_INVALID_SIG, + "Signature by <" + issuer + "> for <" + subject + ">"); + } + } + + + /** + * Creates an X.509 certificate, and signs it using the issuer + * passed (associating a signature algorithm and an X.500 name). + * This operation is used to implement the certificate generation + * functionality of a certificate authority. + * + * @see #getSignedCert + * @see #getSigner + * @see CertAndKeyGen + * + * @param serial the serial number of the certificate (non-null) + * @param issuer the certificate issuer (CA) (non-null) + * @return the signed certificate, as returned by getSignedCert + * + * @exception IOException if any of the data could not be encoded, + * or when any mandatory data was omitted + * @exception SignatureException on signing failures + */ + public byte [] + encodeAndSign ( + BigInt serial, + X500Signer issuer + ) throws IOException, SignatureException + { + rawCert = null; + + /* + * Get the remaining cert parameters, and make sure we have enough. + * + * We deduce version based on what attribute data are available + * For now, we have no attributes, so we always deduce X.509v1 ! + */ + version = 0; + serialnum = serial; + this.issuer = issuer.getSigner (); + issuerSigAlg = issuer.getAlgorithmId (); + + if (subject == null || pubkey == null + || notbefore == null || notafter == null) + throw new IOException ("not enough cert parameters"); + + /* + * Encode the raw cert, create its signature and put it + * into the envelope. + */ + rawCert = DERencode (); + signedCert = sign (issuer, rawCert); + return signedCert; + } + + + /** + * Returns an X500Signer that may be used to create signatures. Those + * signature may in turn be verified using this certificate (or a + * copy of it). + * + * <P><em><b>NOTE:</b> If the private key is by itself capable of + * creating signatures, this fact may not be recognized at this time. + * Specifically, the case of DSS/DSA keys which get their algorithm + * parameters from higher in the certificate chain is not supportable + * without using an X509CertChain API, and there is no current support + * for other sources of algorithm parameters.</em> + * + * @param algorithm the signature algorithm to be used. Note that a + * given public/private key pair may support several such algorithms. + * @param privateKey the private key used to create the signature, + * which must correspond to the public key in this certificate + * @return the Signer object + * + * @exception NoSuchAlgorithmException if the signature + * algorithm is not supported + * @exception InvalidKeyException if either the key in the certificate, + * or the private key parameter, does not support the requested + * signature algorithm + */ + public X500Signer getSigner (AlgorithmId algorithmId, + PrivateKey privateKey) + throws NoSuchAlgorithmException, InvalidKeyException + { + String algorithm; + Signature sig; + + if (privateKey instanceof Key) { + Key key = (Key)privateKey; + algorithm = key.getAlgorithm(); + } else { + throw new InvalidKeyException("private key not a key!"); + } + + sig = Signature.getInstance(algorithmId.getName()); + + if (!pubkey.getAlgorithm ().equals (algorithm)) { + + throw new InvalidKeyException( "Private key algorithm " + + algorithm + + " incompatible with certificate " + + pubkey.getAlgorithm()); + } + sig.initSign (privateKey); + return new X500Signer (sig, subject); + } + + + /** + * Returns a signature object that may be used to verify signatures + * created using a specified signature algorithm and the public key + * contained in this certificate. + * + * <P><em><b>NOTE:</b> If the public key in this certificate is not by + * itself capable of verifying signatures, this may not be recognized + * at this time. Specifically, the case of DSS/DSA keys which get + * their algorithm parameters from higher in the certificate chain + * is not supportable without using an X509CertChain API, and there + * is no current support for other sources of algorithm parameters.</em> + * + * @param algorithm the algorithm of the signature to be verified + * @return the Signature object + * @exception NoSuchAlgorithmException if the signature + * algorithm is not supported + * @exception InvalidKeyException if the key in the certificate + * does not support the requested signature algorithm + */ + public Signature getVerifier(String algorithm) + throws NoSuchAlgorithmException, InvalidKeyException + { + String algName; + Signature sig; + + sig = Signature.getInstance(algorithm); + sig.initVerify (pubkey); + return sig; + } + + + + /** + * Return the signed X.509 certificate as a byte array. + * The bytes are in standard DER marshaled form. + * Null is returned in the case of a partially constructed cert. + */ + public byte [] getSignedCert () + { return signedCert; } + + + /** + * Returns the certificate's serial number. + * Null is returned in the case of a partially constructed cert. + */ + public BigInt getSerialNumber () + { return serialnum; } + + + /** + * Returns the subject's X.500 distinguished name. + */ + public X500Name getSubjectName () + { return subject; } + + + /** + * Returns the certificate issuer's X.500 distinguished name. + * Null is returned in the case of a partially constructed cert. + */ + public X500Name getIssuerName () + { return issuer; } + + + /** + * Returns the algorithm used by the issuer to sign the certificate. + * Null is returned in the case of a partially constructed cert. + */ + public AlgorithmId getIssuerAlgorithmId () + { return issuerSigAlg; } + + + /** + * Returns the first time the certificate is valid. + */ + public Date getNotBefore () + { return notbefore; } + + + /** + * Returns the last time the certificate is valid. + */ + public Date getNotAfter () + { return notafter; } + + + /** + * Returns the subject's public key. Note that some public key + * algorithms support an optional certificate generation policy + * where the keys in the certificates are not in themselves sufficient + * to perform a public key operation. Those keys need to be augmented + * by algorithm parameters, which the certificate generation policy + * chose not to place in the certificate. + * + * <P>Two such public key algorithms are: DSS/DSA, where algorithm + * parameters could be acquired from a CA certificate in the chain + * of issuers; and Diffie-Hellman, with a similar solution although + * the CA then needs both a Diffie-Hellman certificate and a signature + * capable certificate. + */ + public PublicKey getPublicKey () + { return pubkey; } + + + /** + * Returns the X.509 version number of this certificate, zero based. + * That is, "2" indicates an X.509 version 3 (1993) certificate, + * and "0" indicates X.509v1 (1988). + * Zero is returned in the case of a partially constructed cert. + */ + public int getVersion () + { return version; } + + + /** + * Calculates a hash code value for the object. Objects + * which are equal will also have the same hashcode. + */ + public int hashCode () + { + int retval = 0; + + for (int i = 0; i < signedCert.length; i++) + retval += signedCert [i] * i; + return retval; + } + + + /** + * Returns a printable representation of the certificate. This does not + * contain all the information available to distinguish this from any + * other certificate. The certificate must be fully constructed + * before this function may be called; in particular, if you are + * creating certificates you must call encodeAndSign() before calling + * this function. + */ + public String toString () + { + String s; + + if (subject == null || pubkey == null + || notbefore == null || notafter == null + || issuer == null || issuerSigAlg == null + || serialnum == null) + throw new NullPointerException ("X.509 cert is incomplete"); + + s = " X.509v" + (version + 1) + " certificate,\n"; + s += " Subject is " + subject + "\n"; + s += " Key: " + pubkey; + s += " Validity <" + notbefore + "> until <" + notafter + ">\n"; + s += " Issuer is " + issuer + "\n"; + s += " Issuer signature used " + issuerSigAlg.toString () + "\n"; + s += " Serial number = " + serialnum + "\n"; + + // optional v2, v3 extras + + return "[\n" + s + "]"; + } + + + /** + * Returns a printable representation of the certificate. + * + * @param detailed true iff lots of detail is requested + */ + public String toString (boolean detailed) + { return toString (); } + + + /* + * Certificate data, and its envelope + */ + private byte rawCert []; + private byte signature []; + private byte signedCert []; + + /* + * X509.v1 data (parsed) + */ + private X500Name subject; // from subject + private X509Key pubkey; + + private Date notafter; // from CA (constructor) + private Date notbefore; + + private int version; // from CA (signAndEncode) + private BigInt serialnum; + private X500Name issuer; + private AlgorithmId issuerSigAlg; + + /* + * X509.v2 extensions + */ + + /* + * X509.v3 extensions + */ + + /* + * Other extensions ... Netscape, Verisign, SET, etc + */ + + + /************************************************************/ + + /* + * Cert is a SIGNED ASN.1 macro, a three elment sequence: + * + * - Data to be signed (ToBeSigned) -- the "raw" cert + * - Signature algorithm (SigAlgId) + * - The signature bits + * + * This routine unmarshals the certificate, saving the signature + * parts away for later verification. + */ + private void parse (DerValue val) + throws IOException + { + DerValue seq [] = new DerValue [3]; + + seq [0] = val.data.getDerValue (); + seq [1] = val.data.getDerValue (); + seq [2] = val.data.getDerValue (); + + if (val.data.available () != 0) + throw new CertParseError ("signed overrun, bytes = " + + val.data.available ()); + if (seq [0].tag != DerValue.tag_Sequence) + throw new CertParseError ("signed fields invalid"); + + rawCert = seq [0].toByteArray (); // XXX slow; fixme! + + + issuerSigAlg = AlgorithmId.parse (seq [1]); + signature = seq [2].getBitString (); + + if (seq [1].data.available () != 0) { + // XXX why was this error check commented out? + // It was originally part of the next check. + throw new CertParseError ("algid field overrun"); + } + + if (seq [2].data.available () != 0) + throw new CertParseError ("signed fields overrun"); + + /* + * Let's have fun parsing the cert itself. + */ + DerInputStream in; + DerValue tmp; + + in = seq [0].data; + + /* + * Version -- this is optional (default zero). If it's there it's + * the first field and is specially tagged. + * + * Both branches leave "tmp" holding a value for the serial + * number that comes next. + */ + version = 0; + tmp = in.getDerValue (); + if (tmp.isConstructed () && tmp.isContextSpecific ()) { + version = tmp.data.getInteger ().toInt (); + if (tmp.data.available () != 0) + throw new IOException ("X.509 version, bad format"); + tmp = in.getDerValue (); + } + + /* + * serial number ... an integer + */ + serialnum = tmp.getInteger (); + + /* + * algorithm type for CA's signature ... needs to match the + * one on the envelope, and that's about it! different IDs + * may represent a signature attack. In general we want to + * inherit parameters. + */ + tmp = in.getDerValue (); + { + AlgorithmId algid; + + + algid = AlgorithmId.parse(tmp); + + if (!algid.equals (issuerSigAlg)) + throw new CertParseError ("CA Algorithm mismatch!"); + + this.algid = algid; + } + + /* + * issuer name + */ + issuer = new X500Name (in); + + /* + * validity: SEQUENCE { start date, end date } + */ + tmp = in.getDerValue (); + if (tmp.tag != DerValue.tag_Sequence) + throw new CertParseError ("corrupt validity field"); + + notbefore = tmp.data.getUTCTime (); + notafter = tmp.data.getUTCTime (); + if (tmp.data.available () != 0) + throw new CertParseError ("excess validity data"); + + /* + * subject name and public key + */ + subject = new X500Name (in); + + tmp = in.getDerValue (); + pubkey = X509Key.parse (tmp); + + /* + * XXX for v2 and later, a bunch of tagged options follow + */ + + if (in.available () != 0) { + /* + * Until we parse V2/V3 data ... ignore it. + * + // throw new CertParseError ("excess cert data"); + System.out.println ( + "@end'o'cert, optional V2/V3 data unparsed: " + + in.available () + + " bytes" + ); + */ + } + } + + + /* + * Encode only the parts that will later be signed. + */ + private byte [] DERencode () throws IOException + { + DerOutputStream raw = new DerOutputStream (); + + encode (raw); + return raw.toByteArray (); + } + + + /* + * Marshal the contents of a "raw" certificate into a DER sequence. + */ + private void encode (DerOutputStream out) throws IOException + { + DerOutputStream tmp = new DerOutputStream (); + + /* + * encode serial number, issuer signing algorithm, + * and issuer name into the data we'll return + */ + tmp.putInteger (serialnum); + issuerSigAlg.encode (tmp); + issuer.encode (tmp); + + /* + * Validity is a two element sequence ... encode the + * elements, then wrap them into the data we'll return + */ + { + DerOutputStream seq = new DerOutputStream (); + + seq.putUTCTime (notbefore); + seq.putUTCTime (notafter); + tmp.write (DerValue.tag_Sequence, seq); + } + + /* + * Encode subject (principal) and associated key + */ + subject.encode (tmp); + pubkey.encode (tmp); + + /* + * Wrap the data; encoding of the "raw" cert is now complete. + */ + out.write (DerValue.tag_Sequence, tmp); + } + + + /* + * Calculate the signature of the "raw" certificate, + * and marshal the cert with the signature and a + * description of the signing algorithm. + */ + private byte [] sign (X500Signer issuer, byte data []) + throws IOException, SignatureException + { + /* + * Encode the to-be-signed data, then the algorithm used + * to create the signature. + */ + DerOutputStream out = new DerOutputStream (); + DerOutputStream tmp = new DerOutputStream (); + + tmp.write (data); + issuer.getAlgorithmId ().encode(tmp); + + + /* + * Create and encode the signature itself. + */ + issuer.update (data, 0, data.length); + signature = issuer.sign (); + tmp.putBitString (signature); + + /* + * Wrap the signed data in a SEQUENCE { data, algorithm, sig } + */ + out.write (DerValue.tag_Sequence, tmp); + return out.toByteArray (); + } + + + /** + * Serialization write ... X.509 certificates serialize as + * themselves, and they're parsed when they get read back. + * (Actually they serialize as some type data from the + * serialization subsystem, then the cert data.) + */ + private synchronized void + writeObject (java.io.ObjectOutputStream stream) + throws IOException + { encode(stream); } + + /** + * Serialization read ... X.509 certificates serialize as + * themselves, and they're parsed when they get read back. + */ + private synchronized void + readObject (ObjectInputStream stream) + throws IOException + { decode(stream); } +} diff --git a/pki/base/util/src/netscape/security/x509/X509CertImpl.java b/pki/base/util/src/netscape/security/x509/X509CertImpl.java new file mode 100755 index 00000000..4c5c6eb5 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/X509CertImpl.java @@ -0,0 +1,1209 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.Serializable; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.OutputStream; +import java.io.ObjectOutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.math.BigInteger; +import java.security.cert.*; +import java.security.*; +import java.security.PublicKey; +import java.security.PrivateKey; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.Set; + +import netscape.security.util.*; +import netscape.security.util.BigInt; + +/** + * The X509CertImpl class represents an X.509 certificate. These certificates + * are widely used to support authentication and other functionality in + * Internet security systems. Common applications include Privacy Enhanced + * Mail (PEM), Transport Layer Security (SSL), code signing for trusted + * software distribution, and Secure Electronic Transactions (SET). There + * is a commercial infrastructure ready to manage large scale deployments + * of X.509 identity certificates. + * + * <P>These certificates are managed and vouched for by <em>Certificate + * Authorities</em> (CAs). CAs are services which create certificates by + * placing data in the X.509 standard format and then digitally signing + * that data. Such signatures are quite difficult to forge. CAs act as + * trusted third parties, making introductions between agents who have no + * direct knowledge of each other. CA certificates are either signed by + * themselves, or by some other CA such as a "root" CA. + * + * <P>RFC 1422 is very informative, though it does not describe much + * of the recent work being done with X.509 certificates. That includes + * a 1996 version (X.509v3) and a variety of enhancements being made to + * facilitate an explosion of personal certificates used as "Internet + * Drivers' Licences", or with SET for credit card transactions. + * + * <P>More recent work includes the IETF PKIX Working Group efforts, + * especially part 1. + * + * @author Dave Brownell + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.94 97/12/10 + * @see X509CertInfo + */ +public class X509CertImpl extends X509Certificate +implements Serializable, DerEncoder { + // Serialization compatibility with the X509CertImpl in x509v1.jar + // supporting the subset of X509Certificate on JDK1.1.x platforms. + static final long serialVersionUID = -2048442350420423405L; + + private static final String DOT = "."; + /** + * Public attribute names. + */ + public static final String NAME = "x509"; + public static final String INFO = X509CertInfo.NAME; + public static final String ALG_ID = "algorithm"; + public static final String SIGNATURE = "signature"; + public static final String SIGNED_CERT = "signed_cert"; + + /** + * The following are defined for ease-of-use. These + * are the most frequently retrieved attributes. + */ + // x509.info.subject.dname + public static final String SUBJECT_DN = NAME + DOT + INFO + DOT + + X509CertInfo.SUBJECT + DOT + + CertificateSubjectName.DN_NAME; + // x509.info.issuer.dname + public static final String ISSUER_DN = NAME + DOT + INFO + DOT + + X509CertInfo.ISSUER + DOT + + CertificateIssuerName.DN_NAME; + // x509.info.serialNumber.number + public static final String SERIAL_ID = NAME + DOT + INFO + DOT + + X509CertInfo.SERIAL_NUMBER + DOT + + CertificateSerialNumber.NUMBER; + // x509.info.key.value + public static final String PUBLIC_KEY = NAME + DOT + INFO + DOT + + X509CertInfo.KEY + DOT + + CertificateX509Key.KEY; + + // x509.algorithm + public static final String SIG_ALG = NAME + DOT + ALG_ID; + + // x509.signature + public static final String SIG = NAME + DOT + SIGNATURE; + + // when we sign and decode we set this to true + // this is our means to make certificates immutable + private boolean readOnly = false; + + // Certificate data, and its envelope + private byte[] signedCert; + protected X509CertInfo info = null; + protected AlgorithmId algId; + protected byte[] signature; + + // recognized extension OIDS + private static final String KEY_USAGE_OID = "2.5.29.15"; + private static final String BASIC_CONSTRAINT_OID = "2.5.29.19"; + + /** + * Default constructor. + */ + public X509CertImpl() { } + + /** + * Unmarshals a certificate from its encoded form, parsing the + * encoded bytes. This form of constructor is used by agents which + * need to examine and use certificate contents. That is, this is + * one of the more commonly used constructors. Note that the buffer + * must include only a certificate, and no "garbage" may be left at + * the end. If you need to ignore data at the end of a certificate, + * use another constructor. + * + * @param certData the encoded bytes, with no trailing padding. + * @exception CertificateException on parsing and initialization errors. + */ + public X509CertImpl(byte[] certData) + throws CertificateException { + this(certData,null); + } + + /** + * As a special optimization, this constructor acts as X509CertImpl(byte[]) + * except that it takes an X509CertInfo which it uses as a 'hint' for + * how to construct one field. + * + * @param certData the encode bytes, with no traiing padding + * @param certInfo the certInfo which has already been constructed + * from the certData + */ + + public X509CertImpl(byte[] certData, X509CertInfo certInfo) + throws CertificateException { + + // setting info here causes it to skip decoding in the parse() + // method + info = certInfo; + + try { + DerValue in = new DerValue(certData); + + parse(in); + signedCert = certData; + } catch (IOException e) { + throw new CertificateException("Unable to initialize, " + e); + } + } + + /** + * unmarshals an X.509 certificate from an input stream. + * + * @param in an input stream holding at least one certificate + * @exception CertificateException on parsing and initialization errors. + */ + public X509CertImpl(InputStream in) + throws CertificateException { + try { + DerValue val = new DerValue(in); + + parse(val); + signedCert = val.toByteArray(); + } catch (IOException e) { + throw new CertificateException("Unable to initialize, " + e); + } + } + + /** + * Construct an initialized X509 Certificate. The certificate is stored + * in raw form and has to be signed to be useful. + * + * @param certInfo the X509CertificateInfo which the Certificate is to be + * created from. + */ + public X509CertImpl(X509CertInfo certInfo) { + this.info = certInfo; + } + + /** + * Unmarshal a certificate from its encoded form, parsing a DER value. + * This form of constructor is used by agents which need to examine + * and use certificate contents. + * + * @param derVal the der value containing the encoded cert. + * @exception CertificateException on parsing and initialization errors. + */ + public X509CertImpl(DerValue derVal) + throws CertificateException { + try { + parse(derVal); + signedCert = derVal.toByteArray(); + } catch (IOException e) { + throw new CertificateException("Unable to initialize, " + e); + } + } + + public boolean hasUnsupportedCriticalExtension() + { + // XXX NOT IMPLEMENTED + return true; + } + + /** + * Decode an X.509 certificate from an input stream. + * + * @param in an input stream holding at least one certificate + * @exception CertificateException on parsing errors. + * @exception IOException on other errors. + */ + public void decode(InputStream in) + throws CertificateException, IOException { + DerValue val = new DerValue(in); + + parse(val); + signedCert = val.toByteArray(); + } + + /** + * Appends the certificate to an output stream. + * + * @param out an input stream to which the certificate is appended. + * @exception CertificateEncodingException on encoding errors. + */ + public void encode(OutputStream out) + throws CertificateEncodingException { + if (signedCert == null) + throw new CertificateEncodingException( + "Null certificate to encode"); + try { + out.write(signedCert); + } catch (IOException e) { + throw new CertificateEncodingException(e.toString()); + } + } + + /** + * DER encode this object onto an output stream. + * Implements the <code>DerEncoder</code> interface. + * + * @param out + * the output stream on which to write the DER encoding. + * + * @exception IOException on encoding error. + */ + public void derEncode (OutputStream out) throws IOException { + if (signedCert == null) + throw new IOException("Null certificate to encode"); + + out.write(signedCert); + } + + /** + * Returns the encoded form of this certificate. It is + * assumed that each certificate type would have only a single + * form of encoding; for example, X.509 certificates would + * be encoded as ASN.1 DER. + * + * @exception CertificateEncodingException if an encoding error occurs. + */ + public byte[] getEncoded() throws CertificateEncodingException { + if (signedCert == null) + throw new CertificateEncodingException( + "Null certificate to encode"); + byte[] dup = new byte[signedCert.length]; + System.arraycopy(signedCert, 0, dup, 0, dup.length); + return dup; + } + + /** + * Throws an exception if the certificate was not signed using the + * verification key provided. Successfully verifying a certificate + * does <em>not</em> indicate that one should trust the entity which + * it represents. + * + * @param key the public key used for verification. + * + * @exception InvalidKeyException on incorrect key. + * @exception NoSuchAlgorithmException on unsupported signature + * algorithms. + * @exception NoSuchProviderException if there's no default provider. + * @exception SignatureException on signature errors. + * @exception CertificateException on encoding errors. + */ + public void verify(PublicKey key) + throws CertificateException, NoSuchAlgorithmException, + InvalidKeyException, NoSuchProviderException, SignatureException { + + verify(key, null); + } + + /** + * Throws an exception if the certificate was not signed using the + * verification key provided. Successfully verifying a certificate + * does <em>not</em> indicate that one should trust the entity which + * it represents. + * + * @param key the public key used for verification. + * @param sigProvider the name of the provider. + * + * @exception NoSuchAlgorithmException on unsupported signature + * algorithms. + * @exception InvalidKeyException on incorrect key. + * @exception NoSuchProviderException on incorrect provider. + * @exception SignatureException on signature errors. + * @exception CertificateException on encoding errors. + */ + public void verify(PublicKey key, String sigProvider) + throws CertificateException, NoSuchAlgorithmException, + InvalidKeyException, NoSuchProviderException, SignatureException { + if (signedCert == null) { + throw new CertificateEncodingException("Uninitialized certificate"); + } + // Verify the signature ... + Signature sigVerf = null; + + sigVerf = Signature.getInstance(algId.getName(), sigProvider); + sigVerf.initVerify(key); + + byte[] rawCert = info.getEncodedInfo(); + sigVerf.update(rawCert, 0, rawCert.length); + + if (!sigVerf.verify(signature)) { + throw new SignatureException("Signature does not match."); + } + } + + /** + * Creates an X.509 certificate, and signs it using the key + * passed (associating a signature algorithm and an X.500 name). + * This operation is used to implement the certificate generation + * functionality of a certificate authority. + * + * @param key the private key used for signing. + * @param algorithm the name of the signature algorithm used. + * + * @exception InvalidKeyException on incorrect key. + * @exception NoSuchAlgorithmException on unsupported signature + * algorithms. + * @exception NoSuchProviderException if there's no default provider. + * @exception SignatureException on signature errors. + * @exception CertificateException on encoding errors. + */ + public void sign(PrivateKey key, String algorithm) + throws CertificateException, NoSuchAlgorithmException, + InvalidKeyException, NoSuchProviderException, SignatureException { + sign(key, algorithm, null); + } + + /** + * Creates an X.509 certificate, and signs it using the key + * passed (associating a signature algorithm and an X.500 name). + * This operation is used to implement the certificate generation + * functionality of a certificate authority. + * + * @param key the private key used for signing. + * @param algorithm the name of the signature algorithm used. + * @param provider the name of the provider. + * + * @exception NoSuchAlgorithmException on unsupported signature + * algorithms. + * @exception InvalidKeyException on incorrect key. + * @exception NoSuchProviderException on incorrect provider. + * @exception SignatureException on signature errors. + * @exception CertificateException on encoding errors. + */ + public void sign(PrivateKey key, String algorithm, String provider) + throws CertificateException, NoSuchAlgorithmException, + InvalidKeyException, NoSuchProviderException, SignatureException { + try { + if (readOnly) + throw new CertificateEncodingException( + "cannot over-write existing certificate"); + Signature sigEngine = null; + if (provider == null) + sigEngine = Signature.getInstance(algorithm); + else + sigEngine = Signature.getInstance(algorithm, provider); + + sigEngine.initSign(key); + + // in case the name is reset + algId = AlgorithmId.get(sigEngine.getAlgorithm()); + + DerOutputStream out = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + + // encode certificate info + info.encode(tmp); + byte[] rawCert = tmp.toByteArray(); + + // encode algorithm identifier + algId.encode(tmp); + + // Create and encode the signature itself. + sigEngine.update(rawCert, 0, rawCert.length); + signature = sigEngine.sign(); + tmp.putBitString(signature); + + // Wrap the signed data in a SEQUENCE { data, algorithm, sig } + out.write(DerValue.tag_Sequence, tmp); + signedCert = out.toByteArray(); + readOnly = true; + + } catch (IOException e) { + throw new CertificateEncodingException(e.toString()); + } + } + + /** + * Checks that the certificate is currently valid, i.e. the current + * time is within the specified validity period. + * + * @exception CertificateExpiredException if the certificate has expired. + * @exception CertificateNotYetValidException if the certificate is not + * yet valid. + */ + public void checkValidity() + throws CertificateExpiredException, CertificateNotYetValidException { + Date date = new Date(); + checkValidity(date); + } + + /** + * Checks that the specified date is within the certificate's + * validity period, or basically if the certificate would be + * valid at the specified date/time. + * + * @param date the Date to check against to see if this certificate + * is valid at that date/time. + * + * @exception CertificateExpiredException if the certificate has expired + * with respect to the <code>date</code> supplied. + * @exception CertificateNotYetValidException if the certificate is not + * yet valid with respect to the <code>date</code> supplied. + */ + public void checkValidity(Date date) + throws CertificateExpiredException, CertificateNotYetValidException { + + CertificateValidity interval = null; + try { + interval = (CertificateValidity)info.get(CertificateValidity.NAME); + } catch (Exception e) { + throw new CertificateNotYetValidException("Incorrect validity period"); + } + if (interval == null) + throw new CertificateNotYetValidException("Null validity period"); + interval.valid(date); + } + + /** + * Return the requested attribute from the certificate. + * + * @param name the name of the attribute. + * @exception CertificateParsingException on invalid attribute identifier. + */ + public Object get(String name) + throws CertificateParsingException { + X509AttributeName attr = new X509AttributeName(name); + String id = attr.getPrefix(); + if (!(id.equalsIgnoreCase(NAME))) { + throw new CertificateParsingException("Invalid root of " + + "attribute name, expected [" + NAME + + "], received " + "[" + id + "]"); + } + attr = new X509AttributeName(attr.getSuffix()); + id = attr.getPrefix(); + + if (id.equalsIgnoreCase(INFO)) { + if (attr.getSuffix() != null) { + try { + return info.get(attr.getSuffix()); + } catch (IOException e) { + throw new CertificateParsingException(e.toString()); + } catch (CertificateException e) { + throw new CertificateParsingException(e.toString()); + } + } else { + return(info); + } + } else if (id.equalsIgnoreCase(ALG_ID)) { + return(algId); + } else if (id.equalsIgnoreCase(SIGNATURE)) { + return(signature); + } else if (id.equalsIgnoreCase(SIGNED_CERT)) { + return(signedCert); + } else { + throw new CertificateParsingException("Attribute name not " + + "recognized or get() not allowed for the same: " + id); + } + } + + /** + * Set the requested attribute in the certificate. + * + * @param name the name of the attribute. + * @param obj the value of the attribute. + * @exception CertificateException on invalid attribute identifier. + * @exception IOException on encoding error of attribute. + */ + public void set(String name, Object obj) + throws CertificateException, IOException { + // check if immutable + if (readOnly) + throw new CertificateException("cannot over-write existing" + + " certificate"); + + X509AttributeName attr = new X509AttributeName(name); + String id = attr.getPrefix(); + if (!(id.equalsIgnoreCase(NAME))) { + throw new CertificateException("Invalid root of attribute name," + + " expected [" + NAME + "], received " + id); + } + attr = new X509AttributeName(attr.getSuffix()); + id = attr.getPrefix(); + + if (id.equalsIgnoreCase(INFO)) { + if (attr.getSuffix() == null) { + if (!(obj instanceof X509CertInfo)) { + throw new CertificateException("Attribute value should" + + " be of type X509CertInfo."); + } + info = (X509CertInfo)obj; + signedCert = null; //reset this as certificate data has changed + } else { + info.set(attr.getSuffix(), obj); + signedCert = null; //reset this as certificate data has changed + } + } else { + throw new CertificateException("Attribute name not recognized or " + + "set() not allowed for the same: " + id); + } + } + + /** + * Delete the requested attribute from the certificate. + * + * @param name the name of the attribute. + * @exception CertificateException on invalid attribute identifier. + * @exception IOException on other errors. + */ + public void delete(String name) + throws CertificateException, IOException { + // check if immutable + if (readOnly) + throw new CertificateException("cannot over-write existing" + + " certificate"); + + X509AttributeName attr = new X509AttributeName(name); + String id = attr.getPrefix(); + if (!(id.equalsIgnoreCase(NAME))) { + throw new CertificateException("Invalid root of attribute name," + + " expected [" + + NAME + "], received " + id); + } + attr = new X509AttributeName(attr.getSuffix()); + id = attr.getPrefix(); + + if (id.equalsIgnoreCase(INFO)) { + if (attr.getSuffix() != null) { + info = null; + } else { + info.delete(attr.getSuffix()); + } + } else if (id.equalsIgnoreCase(ALG_ID)) { + algId = null; + } else if (id.equalsIgnoreCase(SIGNATURE)) { + signature = null; + } else if (id.equalsIgnoreCase(SIGNED_CERT)) { + signedCert = null; + } else { + throw new CertificateException("Attribute name not recognized or " + + "delete() not allowed for the same: " + id); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(NAME + DOT + INFO); + elements.addElement(NAME + DOT + ALG_ID); + elements.addElement(NAME + DOT + SIGNATURE); + elements.addElement(NAME + DOT + SIGNED_CERT); + + return(elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return(NAME); + } + + /** + * Returns a printable representation of the certificate. This does not + * contain all the information available to distinguish this from any + * other certificate. The certificate must be fully constructed + * before this function may be called. + */ + public String toString() { + if (info == null || algId == null || signature == null) + return ""; + + StringBuffer sb = new StringBuffer(); + + sb.append("[\n"); + sb.append(info.toString() + "\n"); + sb.append(" Algorithm: [" + algId.toString() + "]\n"); + + netscape.security.util.PrettyPrintFormat pp = + new netscape.security.util.PrettyPrintFormat(" ", 20); + String signaturebits = pp.toHexString(signature); + sb.append(" Signature:\n" + signaturebits); + sb.append("]"); + + return sb.toString(); + } + + // the strongly typed gets, as per java.security.cert.X509Certificate + + /** + * Gets the publickey from this certificate. + * + * @return the publickey. + */ + public PublicKey getPublicKey() { + if (info == null) + return null; + try { + PublicKey key = (PublicKey)info.get(CertificateX509Key.NAME + + DOT + CertificateX509Key.KEY); + return key; + } catch (Exception e) { + return null; + } + } + + /** + * Gets the version number from the certificate. + * + * @return the version number. + */ + public int getVersion() { + if (info == null) + return -1; + try { + int vers = ((Integer)info.get(CertificateVersion.NAME + + DOT + CertificateVersion.VERSION)).intValue(); + return vers; + } catch (Exception e) { + return -1; + } + } + + /** + * Gets the serial number from the certificate. + * + * @return the serial number. + */ + public BigInteger getSerialNumber() { + if (info == null) + return null; + try { + SerialNumber ser = (SerialNumber)info.get( + CertificateSerialNumber.NAME + DOT + + CertificateSerialNumber.NUMBER); + return ((BigInt)ser.getNumber()).toBigInteger(); + } catch (Exception e) { + return null; + } + } + + /** + * Gets the subject distinguished name from the certificate. + * + * @return the subject name. + */ + public Principal getSubjectDN() { + if (info == null) + return null; + try { + Principal subject = (Principal)info.get( + CertificateSubjectName.NAME + DOT + + CertificateSubjectName.DN_NAME); + return subject; + } catch (Exception e) { + return null; + } + } + + /** + * Gets the issuer distinguished name from the certificate. + * + * @return the issuer name. + */ + public Principal getIssuerDN() { + if (info == null) + return null; + try { + Principal issuer = (Principal)info.get( + CertificateIssuerName.NAME + DOT + + CertificateIssuerName.DN_NAME); + return issuer; + } catch (Exception e) { + return null; + } + } + + /** + * Gets the notBefore date from the validity period of the certificate. + * + * @return the start date of the validity period. + */ + public Date getNotBefore() { + if (info == null) + return null; + try { + Date d = (Date) info.get(CertificateValidity.NAME + DOT + + CertificateValidity.NOT_BEFORE); + return d; + } catch (Exception e) { + return null; + } + } + + /** + * Gets the notAfter date from the validity period of the certificate. + * + * @return the end date of the validity period. + */ + public Date getNotAfter() { + if (info == null) + return null; + try { + Date d = (Date) info.get(CertificateValidity.NAME + DOT + + CertificateValidity.NOT_AFTER); + return d; + } catch (Exception e) { + return null; + } + } + + /** + * Gets the DER encoded certificate informations, the + * <code>tbsCertificate</code> from this certificate. + * This can be used to verify the signature independently. + * + * @return the DER encoded certificate information. + * @exception CertificateEncodingException if an encoding error occurs. + */ + public byte[] getTBSCertificate() throws CertificateEncodingException { + if (info != null) { + return info.getEncodedInfo(); + } else + throw new CertificateEncodingException("Uninitialized certificate"); + } + + /** + * Gets the raw Signature bits from the certificate. + * + * @return the signature. + */ + public byte[] getSignature() { + if (signature == null) + return null; + byte[] dup = new byte[signature.length]; + System.arraycopy(signature, 0, dup, 0, dup.length); + return dup; + } + + /** + * Gets the signature algorithm name for the certificate + * signature algorithm. + * For example, the string "SHA-1/DSA" or "DSS". + * + * @return the signature algorithm name. + */ + public String getSigAlgName() { + if (algId == null) + return null; + return (algId.getName()); + } + + /** + * Gets the signature algorithm OID string from the certificate. + * For example, the string "1.2.840.10040.4.3" + * + * @return the signature algorithm oid string. + */ + public String getSigAlgOID() { + if (algId == null) + return null; + ObjectIdentifier oid = algId.getOID(); + return (oid.toString()); + } + + /** + * Gets the DER encoded signature algorithm parameters from this + * certificate's signature algorithm. + * + * @return the DER encoded signature algorithm parameters, or + * null if no parameters are present. + */ + public byte[] getSigAlgParams() { + if (algId == null) + return null; + try { + return algId.getEncodedParams(); + } catch (IOException e) { + return null; + } + } + + /** + * Gets the Issuer Unique Identity from the certificate. + * + * @return the Issuer Unique Identity. + */ + public boolean[] getIssuerUniqueID() { + if (info == null) + return null; + try { + UniqueIdentity id = (UniqueIdentity)info.get( + CertificateIssuerUniqueIdentity.NAME + + DOT + CertificateIssuerUniqueIdentity.ID); + if (id == null) + return null; + else + return (id.getId()); + } catch (Exception e) { + return null; + } + } + + /** + * Gets the Subject Unique Identity from the certificate. + * + * @return the Subject Unique Identity. + */ + public boolean[] getSubjectUniqueID() { + if (info == null) + return null; + try { + UniqueIdentity id = (UniqueIdentity)info.get( + CertificateSubjectUniqueIdentity.NAME + + DOT + CertificateSubjectUniqueIdentity.ID); + if (id == null) + return null; + else + return (id.getId()); + } catch (Exception e) { + return null; + } + } + + /** + * Gets a Set of the extension(s) marked CRITICAL in the + * certificate by OID strings. + * + * @return a set of the extension oid strings in the + * certificate that are marked critical. + */ + public Set getCriticalExtensionOIDs() { + if (info == null) + return null; + try { + CertificateExtensions exts = (CertificateExtensions)info.get( + CertificateExtensions.NAME); + if (exts == null) + return null; + ArraySet extSet = new ArraySet(); + Extension ex; + for (Enumeration e = exts.getElements(); e.hasMoreElements(); ) { + ex = (Extension)e.nextElement(); + if (ex.isCritical()) + extSet.add(((ObjectIdentifier)ex.getExtensionId()).toString()); + } + return extSet; + } catch (Exception e) { + return null; + } + } + + /** + * Gets a Set of the extension(s) marked NON-CRITICAL in the + * certificate by OID strings. + * + * @return a set of the extension oid strings in the + * certificate that are NOT marked critical. + */ + public Set getNonCriticalExtensionOIDs() { + if (info == null) + return null; + try { + CertificateExtensions exts = (CertificateExtensions)info.get( + CertificateExtensions.NAME); + if (exts == null) + return null; + + ArraySet extSet = new ArraySet(); + Extension ex; + for (Enumeration e = exts.getElements(); e.hasMoreElements(); ) { + ex = (Extension)e.nextElement(); + if ( ! ex.isCritical()) + extSet.add(((ObjectIdentifier)ex.getExtensionId()).toString()); + } + return extSet; + } catch (Exception e) { + return null; + } + } + + public Extension getExtension(String oid) { + try { + CertificateExtensions exts = (CertificateExtensions)info.get( + CertificateExtensions.NAME); + if (exts == null) + return null; + ObjectIdentifier findOID = new ObjectIdentifier(oid); + Extension ex = null;; + ObjectIdentifier inCertOID; + for (Enumeration e=exts.getElements(); e.hasMoreElements();) { + ex = (Extension)e.nextElement(); + inCertOID = ex.getExtensionId(); + if (inCertOID.equals(findOID)) { + return ex; + } + } + } catch (Exception e) { + } + return null; + } + + /** + * Gets the DER encoded extension identified by the passed + * in oid String. + * + * @param oid the Object Identifier value for the extension. + */ + public byte[] getExtensionValue(String oid) { + try { + String extAlias = OIDMap.getName(new ObjectIdentifier(oid)); + Extension certExt = null; + + if (extAlias == null) { // may be unknown + // get the extensions, search thru' for this oid + CertificateExtensions exts = (CertificateExtensions)info.get( + CertificateExtensions.NAME); + if (exts == null) + return null; + + ObjectIdentifier findOID = new ObjectIdentifier(oid); + Extension ex = null;; + ObjectIdentifier inCertOID; + for (Enumeration e=exts.getElements(); e.hasMoreElements();) { + ex = (Extension)e.nextElement(); + inCertOID = ex.getExtensionId(); + if (inCertOID.equals(findOID)) { + certExt = ex; + break; + } + } + } else { // there's sub-class that can handle this extension + certExt = (Extension)this.get(extAlias); + } + if (certExt == null) + return null; + byte[] extData = certExt.getExtensionValue(); + if (extData == null) + return null; + + DerOutputStream out = new DerOutputStream(); + out.putOctetString(extData); + return out.toByteArray(); + } catch (Exception e) { + return null; + } + } + + /** + * Get a boolean array representing the bits of the KeyUsage extension, + * (oid = 2.5.29.15). + * @return the bit values of this extension as an array of booleans. + */ + public boolean[] getKeyUsage() { + try { + String extAlias = OIDMap.getName(new ObjectIdentifier( + KEY_USAGE_OID)); + if (extAlias == null) + return null; + + KeyUsageExtension certExt = (KeyUsageExtension)this.get(extAlias); + if (certExt == null) + return null; + + return certExt.getBits(); + } catch (Exception e) { + return null; + } + } + + /** + * Get the certificate constraints path length from the + * the critical BasicConstraints extension, (oid = 2.5.29.19). + * @return the length of the constraint. + */ + public int getBasicConstraints() { + try { + String extAlias = OIDMap.getName(new ObjectIdentifier( + BASIC_CONSTRAINT_OID)); + if (extAlias == null) + return -1; + BasicConstraintsExtension certExt = + (BasicConstraintsExtension)this.get(extAlias); + if (certExt == null) + return -1; + + if (((Boolean)certExt.get(BasicConstraintsExtension.IS_CA) + ).booleanValue() == true) + return ((Integer)certExt.get( + BasicConstraintsExtension.PATH_LEN)).intValue(); + else + return -1; + } catch (Exception e) { + return -1; + } + } + + public boolean getBasicConstraintsIsCA() { + boolean isCA = false; + try { + String extAlias = OIDMap.getName(new ObjectIdentifier( + BASIC_CONSTRAINT_OID)); + if (extAlias == null) + return false; + + BasicConstraintsExtension certExt = + (BasicConstraintsExtension)this.get(extAlias); + if (certExt == null) + return false; + + isCA = ((Boolean)certExt.get(BasicConstraintsExtension.IS_CA)).booleanValue(); + } catch (Exception e) { + return false; + } + return isCA; + } + + /************************************************************/ + + /* + * Cert is a SIGNED ASN.1 macro, a three elment sequence: + * + * - Data to be signed (ToBeSigned) -- the "raw" cert + * - Signature algorithm (SigAlgId) + * - The signature bits + * + * This routine unmarshals the certificate, saving the signature + * parts away for later verification. + */ + private void parse(DerValue val) throws CertificateException, IOException { + // check if can over write the certificate + if (readOnly) + throw new CertificateParsingException( + "cannot over-write existing certificate"); + + readOnly = true; + DerValue seq[] = new DerValue[3]; + + seq[0] = val.data.getDerValue(); + seq[1] = val.data.getDerValue(); + seq[2] = val.data.getDerValue(); + + if (val.data.available() != 0) { + throw new CertificateParsingException("signed overrun, bytes = " + + val.data.available()); + } + if (seq[0].tag != DerValue.tag_Sequence) { + throw new CertificateParsingException("signed fields invalid"); + } + + algId = AlgorithmId.parse(seq[1]); + signature = seq[2].getBitString(); + + if (seq[1].data.available() != 0) { + throw new CertificateParsingException("algid field overrun"); + } + if (seq[2].data.available() != 0) + throw new CertificateParsingException("signed fields overrun"); + + // The CertificateInfo + if (info == null) { + info = new X509CertInfo(seq[0]); + } + } + + /** + * Serialization write ... X.509 certificates serialize as + * themselves, and they're parsed when they get read back. + * (Actually they serialize as some type data from the + * serialization subsystem, then the cert data.) + */ + private synchronized void writeObject(ObjectOutputStream stream) + throws CertificateException, IOException { + encode(stream); + } + + /** + * Serialization read ... X.509 certificates serialize as + * themselves, and they're parsed when they get read back. + */ + private synchronized void readObject(ObjectInputStream stream) + throws CertificateException, IOException { + decode(stream); + } + + protected static class CertificateRep1 implements java.io.Serializable { + private String type1; + private byte[] data1; + + /** + * Construct the alternate Certificate class with the Certificate + * type and Certificate encoding bytes. + * + * <p> + * + * @param type the standard name of the Certificate type. <p> + * + * @param data the Certificate data. + */ + protected CertificateRep1(String type, byte[] data) { + this.type1 = type; + this.data1 = data; + } + + /** + * Resolve the Certificate Object. + * + * <p> + * + * @return the resolved Certificate Object. + * + * @throws java.io.ObjectStreamException if the Certificate could not + * be resolved. + */ + protected Object readResolve() throws java.io.ObjectStreamException { + try { + CertificateFactory cf = CertificateFactory.getInstance(type1); + return new X509CertImpl(data1); + +/* + return cf.generateCertificate + (new java.io.ByteArrayInputStream(data1)); +*/ + } catch (CertificateException e) { + throw new java.io.NotSerializableException + ("java.security.cert.Certificate: " + + type1 + + ": " + + e.getMessage()); + } + } + + } + + protected Object writeReplace() throws java.io.ObjectStreamException { + try { + return new CertificateRep1("X.509", getEncoded()); + } catch (CertificateException e) { + throw new java.io.NotSerializableException + ("java.security.cert.Certificate: " + + "X.509" + + ": " + + e.getMessage()); + } + } +} diff --git a/pki/base/util/src/netscape/security/x509/X509CertInfo.java b/pki/base/util/src/netscape/security/x509/X509CertInfo.java new file mode 100644 index 00000000..94019d8b --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/X509CertInfo.java @@ -0,0 +1,955 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.IOException; +import java.io.Serializable; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectInputStream; + +import java.security.cert.*; +import java.util.Enumeration; +import java.util.Hashtable; + +import netscape.security.util.*; + + +/** + * The X509CertInfo class represents X.509 certificate information. + * + * <P>X.509 certificates have several base data elements, including:<UL> + * + * <LI>The <em>Subject Name</em>, an X.500 Distinguished Name for + * the entity (subject) for which the certificate was issued. + * + * <LI>The <em>Subject Public Key</em>, the public key of the subject. + * This is one of the most important parts of the certificate. + * + * <LI>The <em>Validity Period</em>, a time period (e.g. six months) + * within which the certificate is valid (unless revoked). + * + * <LI>The <em>Issuer Name</em>, an X.500 Distinguished Name for the + * Certificate Authority (CA) which issued the certificate. + * + * <LI>A <em>Serial Number</em> assigned by the CA, for use in + * certificate revocation and other applications. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @version 1.16 + * @see CertAttrSet + * @see Serializable + * @see X509CertImpl + */ +public class X509CertInfo implements CertAttrSet, Serializable { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info"; + // Certificate attribute names + public static final String NAME = "info"; + public static final String VERSION = CertificateVersion.NAME; + public static final String SERIAL_NUMBER = CertificateSerialNumber.NAME; + public static final String ALGORITHM_ID = CertificateAlgorithmId.NAME; + public static final String ISSUER = CertificateIssuerName.NAME; + public static final String VALIDITY = CertificateValidity.NAME; + public static final String SUBJECT = CertificateSubjectName.NAME; + public static final String KEY = CertificateX509Key.NAME; + public static final String ISSUER_ID = CertificateIssuerUniqueIdentity.NAME; + public static final String SUBJECT_ID = CertificateSubjectUniqueIdentity.NAME; + public static final String EXTENSIONS = CertificateExtensions.NAME; + + // X509.v1 data + protected CertificateVersion version = new CertificateVersion(); + protected CertificateSerialNumber serialNum = null; + protected CertificateAlgorithmId algId = null; + protected CertificateIssuerName issuer = null; + protected CertificateValidity interval = null; + protected CertificateSubjectName subject = null; + protected CertificateX509Key pubKey = null; + + // X509.v2 & v3 extensions + protected CertificateIssuerUniqueIdentity issuerUniqueId = null; + protected CertificateSubjectUniqueIdentity subjectUniqueId = null; + + // X509.v3 extensions + protected CertificateExtensions extensions = null; + + // Attribute numbers for internal manipulation + private static final int ATTR_VERSION = 1; + private static final int ATTR_SERIAL = 2; + private static final int ATTR_ALGORITHM = 3; + private static final int ATTR_ISSUER = 4; + private static final int ATTR_VALIDITY = 5; + private static final int ATTR_SUBJECT = 6; + private static final int ATTR_KEY = 7; + private static final int ATTR_ISSUER_ID = 8; + private static final int ATTR_SUBJECT_ID = 9; + private static final int ATTR_EXTENSIONS = 10; + + // DER encoded CertificateInfo data + private byte[] rawCertInfo = null; + + // The certificate attribute name to integer mapping stored here + private static final Hashtable map = new Hashtable(); + static { + map.put(VERSION, Integer.valueOf(ATTR_VERSION)); + map.put(SERIAL_NUMBER, Integer.valueOf(ATTR_SERIAL)); + map.put(ALGORITHM_ID, Integer.valueOf(ATTR_ALGORITHM)); + map.put(ISSUER, Integer.valueOf(ATTR_ISSUER)); + map.put(VALIDITY, Integer.valueOf(ATTR_VALIDITY)); + map.put(SUBJECT, Integer.valueOf(ATTR_SUBJECT)); + map.put(KEY, Integer.valueOf(ATTR_KEY)); + map.put(ISSUER_ID, Integer.valueOf(ATTR_ISSUER_ID)); + map.put(SUBJECT_ID, Integer.valueOf(ATTR_SUBJECT_ID)); + map.put(EXTENSIONS, Integer.valueOf(ATTR_EXTENSIONS)); + } + + /** + * Construct an uninitialized X509CertInfo on which <a href="#decode"> + * decode</a> must later be called (or which may be deserialized). + */ + public X509CertInfo() { + } + + /** + * Unmarshals a certificate from its encoded form, parsing the + * encoded bytes. This form of constructor is used by agents which + * need to examine and use certificate contents. That is, this is + * one of the more commonly used constructors. Note that the buffer + * must include only a certificate, and no "garbage" may be left at + * the end. If you need to ignore data at the end of a certificate, + * use another constructor. + * + * @param cert the encoded bytes, with no trailing data. + * @exception CertificateParsingException on parsing errors. + */ + public X509CertInfo(byte[] cert) throws CertificateParsingException { + try { + DerValue in = new DerValue(cert); + + parse(in); + } catch (IOException e) { + throw new CertificateParsingException(e.toString()); + } + } + + /** + * Unmarshal a certificate from its encoded form, parsing a DER value. + * This form of constructor is used by agents which need to examine + * and use certificate contents. + * + * @param derVal the der value containing the encoded cert. + * @exception CertificateParsingException on parsing errors. + */ + public X509CertInfo(DerValue derVal) throws CertificateParsingException { + try { + parse(derVal); + } catch (IOException e) { + throw new CertificateParsingException(e.toString()); + } + } + + /** + * Decode an X.509 certificate from an input stream. + * + * @param in an input stream holding at least one certificate + * @exception CertificateParsingException on decoding errors. + * @exception IOException on other errors. + */ + public void decode(InputStream in) + throws CertificateParsingException, IOException { + DerValue val = new DerValue(in); + + parse(val); + } + + /** + * Appends the certificate to an output stream. + * + * @param out an output stream to which the certificate is appended. + * @exception CertificateException on encoding errors. + * @exception IOException on other errors. + */ + public void encode(OutputStream out) + throws CertificateException, IOException { + encode(out, false); + } + + /** + * Appends the certificate to an output stream. + * @param out An output stream to which the certificate is appended. + * @param ignoreCache Whether to ignore the internal cache when encoding. + * (the cache can easily become out of date). + */ + public void encode(OutputStream out, boolean ignoreCache) + throws IOException, CertificateException { + if (ignoreCache || (rawCertInfo == null)) { + DerOutputStream tmp = new DerOutputStream(); + emit(tmp); + rawCertInfo = tmp.toByteArray(); + } + out.write(rawCertInfo); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(VERSION); + elements.addElement(SERIAL_NUMBER); + elements.addElement(ALGORITHM_ID); + elements.addElement(ISSUER); + elements.addElement(VALIDITY); + elements.addElement(SUBJECT); + elements.addElement(KEY); + elements.addElement(ISSUER_ID); + elements.addElement(SUBJECT_ID); + elements.addElement(EXTENSIONS); + + return(elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return(NAME); + } + + /** + * Returns the encoded certificate info. + * + * @exception CertificateEncodingException on encoding information errors. + */ + public byte[] getEncodedInfo() throws CertificateEncodingException { + return getEncodedInfo(false); + } + + public byte[] getEncodedInfo(boolean ignoreCache) throws CertificateEncodingException { + try { + if (ignoreCache || (rawCertInfo == null)) { + DerOutputStream tmp = new DerOutputStream(); + emit(tmp); + rawCertInfo = tmp.toByteArray(); + } + byte[] dup = new byte[rawCertInfo.length]; + System.arraycopy(rawCertInfo, 0, dup, 0, dup.length); + return dup; + } catch (IOException e) { + throw new CertificateEncodingException(e.toString()); + } catch (CertificateException e) { + throw new CertificateEncodingException(e.toString()); + } + } + + /** + * Compares two X509CertInfo objects. This is false if the + * certificates are not both X.509 certs, otherwise it + * compares them as binary data. + * + * @param other the object being compared with this one + * @return true iff the certificates are equivalent + */ + public boolean equals(Object other) { + if (other instanceof X509CertInfo) { + return equals((X509CertInfo) other); + } else { + return false; + } + } + + /** + * Compares two certificates, returning false if any data + * differs between the two. + * + * @param other the object being compared with this one + * @return true iff the certificates are equivalent + */ + public boolean equals(X509CertInfo other) { + if (this == other) { + return(true); + } else if (rawCertInfo == null || other.rawCertInfo == null) { + return(false); + } else if (rawCertInfo.length != other.rawCertInfo.length) { + return(false); + } + for (int i = 0; i < rawCertInfo.length; i++) { + if (rawCertInfo[i] != other.rawCertInfo[i]) { + return(false); + } + } + return(true); + } + + /** + * Calculates a hash code value for the object. Objects + * which are equal will also have the same hashcode. + */ + public int hashCode() { + int retval = 0; + + for (int i = 1; i < rawCertInfo.length; i++) { + retval += rawCertInfo[i] * i; + } + return(retval); + } + + /** + * Returns a printable representation of the certificate. + */ + public String toString() { + + if (subject == null || pubKey == null || interval == null + || issuer == null || algId == null || serialNum == null) { + throw new NullPointerException("X.509 cert is incomplete"); + } + StringBuffer sb = new StringBuffer(); + + sb.append("[\n"); + sb.append(" " + version.toString() + "\n"); + sb.append(" Subject: " + subject.toString() + "\n"); + sb.append(" Signature Algorithm: " + algId.toString() + "\n"); + sb.append(" Key: " + pubKey.toString() + "\n"); + sb.append(" " + interval.toString() + "\n"); + sb.append(" Issuer: " + issuer.toString() + "\n"); + sb.append(" " + serialNum.toString() + "\n"); + + // optional v2, v3 extras + if (issuerUniqueId != null) { + sb.append(" Issuer Id:\n" + issuerUniqueId.toString() + "\n"); + } + if (subjectUniqueId != null) { + sb.append(" Subject Id:\n" + subjectUniqueId.toString() + "\n"); + } + if (extensions != null) { + netscape.security.util.PrettyPrintFormat pp = + new netscape.security.util.PrettyPrintFormat(" ", 20); + for (int i = 0; i < extensions.size(); i++) { + sb.append(" Extension[" + i + "] = "); + Extension ext = (Extension)extensions.elementAt(i); + try { + if (OIDMap.getClass(ext.getExtensionId()) == null) { + sb.append(ext.toString()); + byte[] extValue = ext.getExtensionValue(); + if (extValue != null) { + DerOutputStream out = new DerOutputStream(); + out.putOctetString(extValue); + extValue = out.toByteArray(); + String extValuebits = pp.toHexString(extValue); + sb.append("Extension unknown: " + + "DER encoded OCTET string =\n" + + extValuebits); + } + } else + sb.append(ext.toString()); //sub-class exists + } catch (Exception e) { + sb.append(", Error parsing this extension"); + } + } + } + sb.append("\n]"); + return sb.toString(); + } + + /** + * Set the certificate attribute. + * + * @param name the name of the Certificate attribute. + * @param val the value of the Certificate attribute. + * @exception CertificateException on invalid attributes. + * @exception IOException on other errors. + */ + public void set(String name, Object val) + throws CertificateException, IOException { + X509AttributeName attrName = new X509AttributeName(name); + + int attr = attributeMap(attrName.getPrefix()); + if (attr == 0) { + throw new CertificateException("Attribute name not recognized: " + + name); + } + // set rawCertInfo to null, so that we are forced to re-encode + rawCertInfo = null; + + switch (attr) { + case ATTR_VERSION: + if (attrName.getSuffix() == null) { + setVersion(val); + } else { + version.set(attrName.getSuffix(),val); + } + break; + + case ATTR_SERIAL: + if (attrName.getSuffix() == null) { + setSerialNumber(val); + } else { + serialNum.set(attrName.getSuffix(),val); + } + break; + + case ATTR_ALGORITHM: + if (attrName.getSuffix() == null) { + setAlgorithmId(val); + } else { + algId.set(attrName.getSuffix(),val); + } + break; + + case ATTR_ISSUER: + if (attrName.getSuffix() == null) { + setIssuer(val); + } else { + issuer.set(attrName.getSuffix(),val); + } + break; + + case ATTR_VALIDITY: + if (attrName.getSuffix() == null) { + setValidity(val); + } else { + interval.set(attrName.getSuffix(),val); + } + break; + + case ATTR_SUBJECT: + if (attrName.getSuffix() == null) { + setSubject(val); + } else { + subject.set(attrName.getSuffix(),val); + } + break; + + case ATTR_KEY: + if (attrName.getSuffix() == null) { + setKey(val); + } else { + pubKey.set(attrName.getSuffix(),val); + } + break; + + case ATTR_ISSUER_ID: + if (attrName.getSuffix() == null) { + setIssuerUniqueId(val); + } else { + issuerUniqueId.set(attrName.getSuffix(),val); + } + break; + + case ATTR_SUBJECT_ID: + if (attrName.getSuffix() == null) { + setSubjectUniqueId(val); + } else { + subjectUniqueId.set(attrName.getSuffix(),val); + } + break; + + case ATTR_EXTENSIONS: + if (attrName.getSuffix() == null) { + setExtensions(val); + } else { + extensions.set(attrName.getSuffix(),val); + } + break; + } + } + + /** + * Delete the certificate attribute. + * + * @param name the name of the Certificate attribute. + * @exception CertificateException on invalid attributes. + * @exception IOException on other errors. + */ + public void delete(String name) + throws CertificateException, IOException { + X509AttributeName attrName = new X509AttributeName(name); + + int attr = attributeMap(attrName.getPrefix()); + if (attr == 0) { + throw new CertificateException("Attribute name not recognized: " + + name); + } + // set rawCertInfo to null, so that we are forced to re-encode + rawCertInfo = null; + + switch (attr) { + case ATTR_VERSION: + if (attrName.getSuffix() == null) { + version = null; + } else { + version.delete(attrName.getSuffix()); + } + break; + case (ATTR_SERIAL): + if (attrName.getSuffix() == null) { + serialNum = null; + } else { + serialNum.delete(attrName.getSuffix()); + } + break; + case (ATTR_ALGORITHM): + if (attrName.getSuffix() == null) { + algId = null; + } else { + algId.delete(attrName.getSuffix()); + } + break; + case (ATTR_ISSUER): + if (attrName.getSuffix() == null) { + issuer = null; + } else { + issuer.delete(attrName.getSuffix()); + } + break; + case (ATTR_VALIDITY): + if (attrName.getSuffix() == null) { + interval = null; + } else { + interval.delete(attrName.getSuffix()); + } + break; + case (ATTR_SUBJECT): + if (attrName.getSuffix() == null) { + subject = null; + } else { + subject.delete(attrName.getSuffix()); + } + break; + case (ATTR_KEY): + if (attrName.getSuffix() == null) { + pubKey = null; + } else { + pubKey.delete(attrName.getSuffix()); + } + break; + case (ATTR_ISSUER_ID): + if (attrName.getSuffix() == null) { + issuerUniqueId = null; + } else { + issuerUniqueId.delete(attrName.getSuffix()); + } + break; + case (ATTR_SUBJECT_ID): + if (attrName.getSuffix() == null) { + subjectUniqueId = null; + } else { + subjectUniqueId.delete(attrName.getSuffix()); + } + break; + case (ATTR_EXTENSIONS): + if (attrName.getSuffix() == null) { + extensions = null; + } else { + extensions.delete(attrName.getSuffix()); + } + break; + } + } + + /** + * Get the certificate attribute. + * + * @param name the name of the Certificate attribute. + * + * @exception CertificateException on invalid attributes. + * @exception IOException on other errors. + */ + public Object get(String name) + throws CertificateException, IOException { + X509AttributeName attrName = new X509AttributeName(name); + + int attr = attributeMap(attrName.getPrefix()); + if (attr == 0) { + throw new CertificateParsingException( + "Attribute name not recognized: " + name); + } + + switch (attr) { + case (ATTR_VERSION): + if (attrName.getSuffix() == null) { + return(version); + } else { + return(version.get(attrName.getSuffix())); + } + case (ATTR_SERIAL): + if (attrName.getSuffix() == null) { + return(serialNum); + } else { + return(serialNum.get(attrName.getSuffix())); + } + case (ATTR_ALGORITHM): + if (attrName.getSuffix() == null) { + return(algId); + } else { + return(algId.get(attrName.getSuffix())); + } + case (ATTR_ISSUER): + if (attrName.getSuffix() == null) { + return(issuer); + } else { + return(issuer.get(attrName.getSuffix())); + } + case (ATTR_VALIDITY): + if (attrName.getSuffix() == null) { + return(interval); + } else { + return(interval.get(attrName.getSuffix())); + } + case (ATTR_SUBJECT): + if (attrName.getSuffix() == null) { + return(subject); + } else { + return(subject.get(attrName.getSuffix())); + } + case (ATTR_KEY): + if (attrName.getSuffix() == null) { + return(pubKey); + } else { + return(pubKey.get(attrName.getSuffix())); + } + case (ATTR_ISSUER_ID): + if (attrName.getSuffix() == null) { + return(issuerUniqueId); + } else { + if (issuerUniqueId == null) + return null; + else + return(issuerUniqueId.get(attrName.getSuffix())); + } + case (ATTR_SUBJECT_ID): + if (attrName.getSuffix() == null) { + return(subjectUniqueId); + } else { + if (subjectUniqueId == null) + return null; + else + return(subjectUniqueId.get(attrName.getSuffix())); + } + case (ATTR_EXTENSIONS): + if (attrName.getSuffix() == null) { + return(extensions); + } else { + if (extensions == null) + return null; + else + return(extensions.get(attrName.getSuffix())); + } + } + return null; + } + + /* + * This routine unmarshals the certificate information. + */ + private void parse(DerValue val) + throws CertificateParsingException, IOException { + DerInputStream in; + DerValue tmp; + + if (val.tag != DerValue.tag_Sequence) { + throw new CertificateParsingException("signed fields invalid"); + } + rawCertInfo = val.toByteArray(); + + in = val.data; + + // Version + tmp = in.getDerValue(); + if (tmp.isContextSpecific((byte)0)) { + version = new CertificateVersion(tmp); + tmp = in.getDerValue(); + } + + // Serial number ... an integer + serialNum = new CertificateSerialNumber(tmp); + + // Algorithm Identifier + algId = new CertificateAlgorithmId(in); + + // Issuer name + issuer = new CertificateIssuerName(in); + + // validity: SEQUENCE { start date, end date } + interval = new CertificateValidity(in); + + // subject name + subject = new CertificateSubjectName(in); + + // public key + pubKey = new CertificateX509Key(in); + + // If more data available, make sure version is not v1. + if (in.available() != 0) { + if (version.compare(CertificateVersion.V1) == 0) { + throw new CertificateParsingException("excess cert data"); + } + } else { + return; + } + + // Get the issuerUniqueId if present + tmp = in.getDerValue(); + if (tmp.isContextSpecific((byte)1)) { + issuerUniqueId = new CertificateIssuerUniqueIdentity(tmp); + if (in.available() == 0) { + return; + } + tmp = in.getDerValue(); + } + + // Get the subjectUniqueId if present. + if (tmp.isContextSpecific((byte)2)) { + subjectUniqueId = new CertificateSubjectUniqueIdentity(tmp); + if (in.available() == 0) { + return; + } + tmp = in.getDerValue(); + } + + // Get the extensions. + if (version.compare(CertificateVersion.V3) != 0) { + throw new CertificateParsingException("excess cert data"); + } + if (tmp.isConstructed() && tmp.isContextSpecific((byte)3)) { + extensions = new CertificateExtensions(tmp.data); + } + } + + /* + * Marshal the contents of a "raw" certificate into a DER sequence. + */ + private void emit(DerOutputStream out) + throws CertificateException, IOException { + DerOutputStream tmp = new DerOutputStream(); + + // version number, iff not V1 + version.encode(tmp); + + // Encode serial number, issuer signing algorithm, issuer name + // and validity + serialNum.encode(tmp); + algId.encode(tmp); + issuer.encode(tmp); + interval.encode(tmp); + + // Encode subject (principal) and associated key + subject.encode(tmp); + pubKey.encode(tmp); + + // Encode issuerUniqueId & subjectUniqueId. + if (issuerUniqueId != null) { + issuerUniqueId.encode(tmp); + } + if (subjectUniqueId != null) { + subjectUniqueId.encode(tmp); + } + + // Write all the extensions. + if (extensions != null) { + extensions.encode(tmp); + } + + // Wrap the data; encoding of the "raw" cert is now complete. + out.write(DerValue.tag_Sequence, tmp); + } + + /** + * Serialization write ... X.509 certificates serialize as + * themselves, and they're parsed when they get read back. + * (Actually they serialize as some type data from the + * serialization subsystem, then the cert data.) + */ + private synchronized void writeObject(ObjectOutputStream stream) + throws CertificateException, IOException { + encode(stream); + } + + /** + * Serialization read ... X.509 certificates serialize as + * themselves, and they're parsed when they get read back. + */ + private synchronized void readObject(ObjectInputStream stream) + throws CertificateException, IOException { + decode(stream); + } + + /** + * Returns the integer attribute number for the passed attribute name. + */ + private int attributeMap(String name) { + Integer num = (Integer)map.get(name); + if (num == null) { + return(0); + } + return(num.intValue()); + } + + /** + * Set the version number of the certificate. + * + * @param val the Object class value for the Extensions + * @exception CertificateException on invalid data. + */ + private void setVersion(Object val) throws CertificateException { + if (!(val instanceof CertificateVersion)) { + throw new CertificateException("Version class type invalid."); + } + version = (CertificateVersion)val; + } + + /** + * Set the serial number of the certificate. + * + * @param val the Object class value for the CertificateSerialNumber + * @exception CertificateException on invalid data. + */ + private void setSerialNumber(Object val) throws CertificateException { + if (!(val instanceof CertificateSerialNumber)) { + throw new CertificateException("SerialNumber class type invalid."); + } + serialNum = (CertificateSerialNumber)val; + } + + /** + * Set the algorithm id of the certificate. + * + * @param val the Object class value for the AlgorithmId + * @exception CertificateException on invalid data. + */ + private void setAlgorithmId(Object val) throws CertificateException { + if (!(val instanceof CertificateAlgorithmId)) { + throw new CertificateException( + "AlgorithmId class type invalid."); + } + algId = (CertificateAlgorithmId)val; + } + + /** + * Set the issuer name of the certificate. + * + * @param val the Object class value for the issuer + * @exception CertificateException on invalid data. + */ + private void setIssuer(Object val) throws CertificateException { + if (!(val instanceof CertificateIssuerName)) { + throw new CertificateException( + "Issuer class type invalid."); + } + issuer = (CertificateIssuerName)val; + } + + /** + * Set the validity interval of the certificate. + * + * @param val the Object class value for the CertificateValidity + * @exception CertificateException on invalid data. + */ + private void setValidity(Object val) throws CertificateException { + if (!(val instanceof CertificateValidity)) { + throw new CertificateException( + "CertificateValidity class type invalid."); + } + interval = (CertificateValidity)val; + } + + /** + * Set the subject name of the certificate. + * + * @param val the Object class value for the Subject + * @exception CertificateException on invalid data. + */ + private void setSubject(Object val) throws CertificateException { + if (!(val instanceof CertificateSubjectName)) { + throw new CertificateException( + "Subject class type invalid."); + } + subject = (CertificateSubjectName)val; + } + + /** + * Set the public key in the certificate. + * + * @param val the Object class value for the PublicKey + * @exception CertificateException on invalid data. + */ + private void setKey(Object val) throws CertificateException { + if (!(val instanceof CertificateX509Key)) { + throw new CertificateException( + "Key class type invalid."); + } + pubKey = (CertificateX509Key)val; + } + + /** + * Set the Issuer Unique Identity in the certificate. + * + * @param val the Object class value for the IssuerUniqueId + * @exception CertificateException + */ + private void setIssuerUniqueId(Object val) throws CertificateException { + if (version.compare(CertificateVersion.V2) < 0) { + throw new CertificateException("Invalid version"); + } + if (!(val instanceof CertificateIssuerUniqueIdentity)) { + throw new CertificateException( + "IssuerUniqueId class type invalid."); + } + issuerUniqueId = (CertificateIssuerUniqueIdentity)val; + } + + /** + * Set the Subject Unique Identity in the certificate. + * + * @param val the Object class value for the SubjectUniqueId + * @exception CertificateException + */ + private void setSubjectUniqueId(Object val) throws CertificateException { + if (version.compare(CertificateVersion.V2) < 0) { + throw new CertificateException("Invalid version"); + } + if (!(val instanceof CertificateSubjectUniqueIdentity)) { + throw new CertificateException( + "SubjectUniqueId class type invalid."); + } + subjectUniqueId = (CertificateSubjectUniqueIdentity)val; + } + + /** + * Set the extensions in the certificate. + * + * @param val the Object class value for the Extensions + * @exception CertificateException + */ + private void setExtensions(Object val) throws CertificateException { + if (version.compare(CertificateVersion.V3) < 0) { + throw new CertificateException("Invalid version"); + } + if (!(val instanceof CertificateExtensions)) { + throw new CertificateException( + "Extensions class type invalid."); + } + extensions = (CertificateExtensions)val; + } +} diff --git a/pki/base/util/src/netscape/security/x509/X509ExtensionException.java b/pki/base/util/src/netscape/security/x509/X509ExtensionException.java new file mode 100644 index 00000000..007794e2 --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/X509ExtensionException.java @@ -0,0 +1,49 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.security.GeneralSecurityException; + +/** + * X.509 Extension Exception. + * + * @author Hemma Prafullchandra + * 1.2 + */ +public class X509ExtensionException extends GeneralSecurityException { + + /** + * Constructs an X509ExtensionException with no detail message. A + * detail message is a String that describes this particular + * exception. + */ + public X509ExtensionException() { + super(); + } + + /** + * Constructs the exception with the specified detail + * message. A detail message is a String that describes this + * particular exception. + * + * @param message the detail message. + */ + public X509ExtensionException(String message) { + super(message); + } +} diff --git a/pki/base/util/src/netscape/security/x509/X509Key.java b/pki/base/util/src/netscape/security/x509/X509Key.java new file mode 100644 index 00000000..3bdeed4d --- /dev/null +++ b/pki/base/util/src/netscape/security/x509/X509Key.java @@ -0,0 +1,513 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.x509; + +import java.io.*; +import java.util.Properties; +import java.security.Key; +import java.security.PublicKey; +import java.security.KeyFactory; +import java.security.Security; +import java.security.Provider; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; + +import netscape.security.util.*; + +/** + * Holds an X.509 key, for example a public key found in an X.509 + * certificate. Includes a description of the algorithm to be used + * with the key; these keys normally are used as + * "SubjectPublicKeyInfo". + * + * <P>While this class can represent any kind of X.509 key, it may be + * desirable to provide subclasses which understand how to parse keying + * data. For example, RSA public keys have two members, one for the + * public modulus and one for the prime exponent. If such a class is + * provided, it is used when parsing X.509 keys. If one is not provided, + * the key still parses correctly. + * + * @version 1.74, 97/12/10 + * @author David Brownell + */ +public class X509Key implements PublicKey { + + /** use serialVersionUID from JDK 1.1. for interoperability */ + private static final long serialVersionUID = -5359250853002055002L; + + /* The algorithm information (name, parameters, etc). */ + protected AlgorithmId algid; + + /* The key bytes, without the algorithm information */ + protected byte[] key; + + /* The encoding for the key. */ + protected byte[] encodedKey; + + /** + * Default constructor. The key constructed must have its key + * and algorithm initialized before it may be used, for example + * by using <code>decode</code>. + */ + public X509Key() { } + + /* + * Build and initialize as a "default" key. All X.509 key + * data is stored and transmitted losslessly, but no knowledge + * about this particular algorithm is available. + */ + public X509Key(AlgorithmId algid, byte[] key) + throws InvalidKeyException { + this.algid = algid; + this.key = key; + encode(); + } + + /** + * Construct X.509 subject public key from a DER value. If + * the runtime environment is configured with a specific class for + * this kind of key, a subclass is returned. Otherwise, a generic + * X509Key object is returned. + * + * <P>This mechanism gurantees that keys (and algorithms) may be + * freely manipulated and transferred, without risk of losing + * information. Also, when a key (or algorithm) needs some special + * handling, that specific need can be accomodated. + * + * @param in the DER-encoded SubjectPublicKeyInfo value + * @exception IOException on data format errors + */ + public static X509Key parse(DerValue in) throws IOException + { + AlgorithmId algorithm; + X509Key subjectKey; + + if (in.tag != DerValue.tag_Sequence) + throw new IOException("corrupt subject key"); + + algorithm = AlgorithmId.parse(in.data.getDerValue ()); + try { + subjectKey = buildX509Key(algorithm, in.data.getBitString ()); + + } catch (InvalidKeyException e) { + throw new IOException("subject key, " + e.getMessage()); + } + + if (in.data.available () != 0) + throw new IOException ("excess subject key"); + return subjectKey; + } + + /** + * Parse the key bits. This may be redefined by subclasses to take + * advantage of structure within the key. For example, RSA public + * keys encapsulate two unsigned integers (modulus and exponent) as + * DER values within the <code>key</code> bits; Diffie-Hellman and + * DSS/DSA keys encapsulate a single unsigned integer. + * + * <P>This function is called when creating X.509 SubjectPublicKeyInfo + * values using the X509Key member functions, such as <code>parse</code> + * and <code>decode</code>. + * + * @exception IOException on parsing errors. + * @exception InvalidKeyException on invalid key encodings. + */ + protected void parseKeyBits() throws IOException, InvalidKeyException { + encode(); + } + + /* + * Factory interface, building the kind of key associated with this + * specific algorithm ID or else returning this generic base class. + * See the description above. + */ + static X509Key buildX509Key(AlgorithmId algid, byte[] key) + throws IOException, InvalidKeyException + { + /* + * Use the algid and key parameters to produce the ASN.1 encoding + * of the key, which will then be used as the input to the + * key factory. + */ + DerOutputStream x509EncodedKeyStream = new DerOutputStream(); + encode(x509EncodedKeyStream, algid, key); + X509EncodedKeySpec x509KeySpec + = new X509EncodedKeySpec(x509EncodedKeyStream.toByteArray()); + + try { + // Instantiate the key factory of the appropriate algorithm + KeyFactory keyFac = null; + if (Security.getProvider("Mozilla-JSS") == null) { + keyFac = KeyFactory.getInstance(algid.getName()); + } else { + keyFac = KeyFactory.getInstance(algid.getName(), + "Mozilla-JSS"); + } + + // Generate the public key + PublicKey pubKey = keyFac.generatePublic(x509KeySpec); + + if (pubKey instanceof X509Key) { + /* + * Return specialized X509Key, where the structure within the + * key has been parsed + */ + return (X509Key)pubKey; + } + } catch (NoSuchAlgorithmException e) { + // Return generic X509Key with opaque key data (see below) + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException(e.toString()); + } catch (Exception e) { + throw new InvalidKeyException(e.toString()); + } + + /* + * Try again using JDK1.1-style for backwards compatibility. + */ + String classname = ""; + try { + Properties props; + String keytype; + Provider sunProvider; + + sunProvider = Security.getProvider("SUN"); + if (sunProvider == null) + throw new InstantiationException(); + classname = sunProvider.getProperty("PublicKey.X.509." + + algid.getName()); + if (classname == null) { + throw new InstantiationException(); + } + + Class keyClass = Class.forName(classname); + Object inst; + X509Key result; + + inst = keyClass.newInstance(); + if (inst instanceof X509Key) { + result = (X509Key) inst; + result.algid = algid; + result.key = key; + result.parseKeyBits(); + return result; + } + } catch (ClassNotFoundException e) { + } catch (InstantiationException e) { + } catch (IllegalAccessException e) { + // this should not happen. + throw new IOException (classname + " [internal error]"); + } + + X509Key result = new X509Key(); + result.algid = algid; + result.key = key; + return result; + } + + /** + * Returns the algorithm to be used with this key. + */ + public String getAlgorithm() { + return algid.getName(); + } + + /** + * Returns the algorithm ID to be used with this key. + */ + public AlgorithmId getAlgorithmId () { return algid; } + + /** + * Encode SubjectPublicKeyInfo sequence on the DER output stream. + * + * @exception IOException on encoding errors. + */ + public final void encode(DerOutputStream out) throws IOException + { + encode(out, this.algid, this.key); + } + + /** + * Returns the DER-encoded form of the key as a byte array. + */ + public synchronized byte[] getEncoded() { + byte[] result = null; + try { + result = encode(); + } catch (InvalidKeyException e) { + } + return result; + } + + /** + * Returns the format for this key: "X.509" + */ + public String getFormat() { + return "X.509"; + } + + /** + * Returns the raw key as a byte array + */ + public byte[] getKey() { + return key; + } + + /** + * Returns the DER-encoded form of the key as a byte array. + * + * @exception InvalidKeyException on encoding errors. + */ + public byte[] encode() throws InvalidKeyException { + if (encodedKey == null) { + try { + DerOutputStream out; + + out = new DerOutputStream (); + encode (out); + encodedKey = out.toByteArray(); + + } catch (IOException e) { + throw new InvalidKeyException ("IOException : " + + e.getMessage()); + } + } + return copyEncodedKey(encodedKey); + } + + /* + * Returns a printable representation of the key + */ + public String toString () + { + netscape.security.util.PrettyPrintFormat pp = + new netscape.security.util.PrettyPrintFormat(" ", 20); + String keybits = pp.toHexString(key); + + return "algorithm = " + algid.toString () + + ", unparsed keybits = \n" + keybits; + } + + /** + * Initialize an X509Key object from an input stream. The data on that + * input stream must be encoded using DER, obeying the X.509 + * <code>SubjectPublicKeyInfo</code> format. That is, the data is a + * sequence consisting of an algorithm ID and a bit string which holds + * the key. (That bit string is often used to encapsulate another DER + * encoded sequence.) + * + * <P>Subclasses should not normally redefine this method; they should + * instead provide a <code>parseKeyBits</code> method to parse any + * fields inside the <code>key</code> member. + * + * <P>The exception to this rule is that since private keys need not + * be encoded using the X.509 <code>SubjectPublicKeyInfo</code> format, + * private keys may override this method, <code>encode</code>, and + * of course <code>getFormat</code>. + * + * @param in an input stream with a DER-encoded X.509 + * SubjectPublicKeyInfo value + * @exception InvalidKeyException on parsing errors. + */ + public void decode(InputStream in) + throws InvalidKeyException + { + DerValue val; + + try { + val = new DerValue (in); + if (val.tag != DerValue.tag_Sequence) + throw new InvalidKeyException ("invalid key format"); + + algid = AlgorithmId.parse (val.data.getDerValue ()); + key = val.data.getBitString (); + parseKeyBits (); + if (val.data.available () != 0) + throw new InvalidKeyException ("excess key data"); + + } catch (IOException e) { + // e.printStackTrace (); + throw new InvalidKeyException("IOException : " + + e.getMessage()); + } + } + + public void decode(byte[] encodedKey) throws InvalidKeyException { + decode(new ByteArrayInputStream(encodedKey)); + } + + /** + * Serialization write ... X.509 keys serialize as + * themselves, and they're parsed when they get read back. + */ + private synchronized void + writeObject (java.io.ObjectOutputStream stream) + throws IOException { + stream.write(getEncoded()); + } + + /** + * Serialization read ... X.509 keys serialize as + * themselves, and they're parsed when they get read back. + */ + private synchronized void + readObject (ObjectInputStream stream) + throws IOException { + + try { + decode(stream); + + } catch (InvalidKeyException e) { + e.printStackTrace(); + throw new IOException("deserialized key is invalid: " + + e.getMessage()); + } + } + + public boolean equals(Object object) { + if (this == object) { + return true; + } + + if (object instanceof Key) { + Key key = (Key)object; + + byte[] b1; + if (encodedKey != null) { + b1 = encodedKey; + } else { + b1 = getEncoded(); + } + byte[] b2 = key.getEncoded(); + + return java.security.MessageDigest.isEqual(b1, b2); + } + + return false; + } + + /** + * Calculates a hash code value for the object. Objects + * which are equal will also have the same hashcode. + */ + public int hashCode() { + int retval = 0; + byte[] b1 = getEncoded(); + + for (int i = 1; i < b1.length; i++) { + retval += b1[i] * i; + } + return(retval); + } + + /* + * Make a copy of the encoded key. + */ + private byte[] copyEncodedKey(byte[] encodedKey) { + int len = encodedKey.length; + byte[] copy = new byte[len]; + System.arraycopy(encodedKey, 0, copy, 0, len); + return copy; + } + + /* + * Produce SubjectPublicKey encoding from algorithm id and key material. + */ + static void encode(DerOutputStream out, AlgorithmId algid, byte[] key) + throws IOException { + DerOutputStream tmp = new DerOutputStream(); + algid.encode(tmp); + tmp.putBitString(key); + out.write(DerValue.tag_Sequence, tmp); + } + /* + * parsePublicKey returns a PublicKey for use with package JSS from within netscape.security.*. + * This function provide an interim solution for migrating from using the netscape.security.* package + * to using the JSS package. + */ + + public static PublicKey parsePublicKey(DerValue in) throws IOException + { + AlgorithmId algorithm; + PublicKey subjectKey; + + if (in.tag != DerValue.tag_Sequence) + throw new IOException("corrupt subject key"); + + algorithm = AlgorithmId.parse(in.data.getDerValue ()); + try { + subjectKey = buildPublicKey(algorithm, in.data.getBitString ()); + + } catch (InvalidKeyException e) { + throw new IOException("subject key, " + e.getMessage()); + } + + if (in.data.available () != 0) + throw new IOException ("excess subject key"); + return subjectKey; + } + /* buildPublicKey returns a PublicKey for use with the JSS package from within netscape.security.*. + * This function provide an interim solution for migrating from using the netscape.security.* package + * to using the JSS package. + */ + static PublicKey buildPublicKey(AlgorithmId algid, byte[] key) + throws IOException, InvalidKeyException + { + /* + * Use the algid and key parameters to produce the ASN.1 encoding + * of the key, which will then be used as the input to the + * key factory. + */ + DerOutputStream x509EncodedKeyStream = new DerOutputStream(); + encode(x509EncodedKeyStream, algid, key); + X509EncodedKeySpec x509KeySpec + = new X509EncodedKeySpec(x509EncodedKeyStream.toByteArray()); + + try { + // Instantiate the key factory of the appropriate algorithm + KeyFactory keyFac = null; + if (Security.getProvider("Mozilla-JSS") == null) { + keyFac = KeyFactory.getInstance(algid.getName()); + } else { + keyFac = KeyFactory.getInstance(algid.getName(), + "Mozilla-JSS"); + } + + // Generate the public key + PublicKey pubKey = keyFac.generatePublic(x509KeySpec); + + /* + * Return specialized X509Key, where the structure within the + * key has been parsed + */ + return pubKey; + } catch (NoSuchAlgorithmException e) { + // Return generic X509Key with opaque key data (see below) + throw new InvalidKeyException(e.toString()); + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException(e.toString()); + } catch (Exception e) { + throw new InvalidKeyException(e.toString()); + } + + } + + +} |