From d0f2e4efbd3eb0f1d7f5a28e7f97c1fb4ec027bb Mon Sep 17 00:00:00 2001 From: PKI Team Date: Tue, 18 Mar 2008 22:36:57 +0000 Subject: 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 --- .../netscape/security/util/ASN1CharStrConvMap.java | 225 +++ .../util/src/netscape/security/util/ArraySet.java | 193 +++ .../util/src/netscape/security/util/BigInt.java | 203 +++ .../util/src/netscape/security/util/BitArray.java | 258 ++++ .../netscape/security/util/ByteArrayLexOrder.java | 63 + .../netscape/security/util/ByteArrayTagOrder.java | 49 + .../security/util/ByteToCharIA5String.java | 69 + .../security/util/ByteToCharPrintable.java | 88 ++ .../netscape/security/util/ByteToCharUnicode.java | 189 +++ .../security/util/ByteToCharUniversalString.java | 97 ++ .../netscape/security/util/CertPrettyPrint.java | 338 +++++ .../security/util/CharToByteIA5String.java | 88 ++ .../security/util/CharToBytePrintable.java | 122 ++ .../security/util/CharToByteUniversalString.java | 85 ++ .../src/netscape/security/util/CrlPrettyPrint.java | 270 ++++ .../src/netscape/security/util/DerEncoder.java | 40 + .../src/netscape/security/util/DerInputBuffer.java | 185 +++ .../src/netscape/security/util/DerInputStream.java | 662 +++++++++ .../netscape/security/util/DerOutputStream.java | 749 ++++++++++ .../util/src/netscape/security/util/DerValue.java | 715 +++++++++ .../src/netscape/security/util/ExtPrettyPrint.java | 1556 ++++++++++++++++++++ .../netscape/security/util/ObjectIdentifier.java | 447 ++++++ .../netscape/security/util/PrettyPrintFormat.java | 170 +++ .../security/util/PrettyPrintResources.java | 300 ++++ .../netscape/security/util/PubKeyPrettyPrint.java | 126 ++ 25 files changed, 7287 insertions(+) create mode 100644 pki/base/util/src/netscape/security/util/ASN1CharStrConvMap.java create mode 100644 pki/base/util/src/netscape/security/util/ArraySet.java create mode 100644 pki/base/util/src/netscape/security/util/BigInt.java create mode 100644 pki/base/util/src/netscape/security/util/BitArray.java create mode 100644 pki/base/util/src/netscape/security/util/ByteArrayLexOrder.java create mode 100644 pki/base/util/src/netscape/security/util/ByteArrayTagOrder.java create mode 100644 pki/base/util/src/netscape/security/util/ByteToCharIA5String.java create mode 100644 pki/base/util/src/netscape/security/util/ByteToCharPrintable.java create mode 100644 pki/base/util/src/netscape/security/util/ByteToCharUnicode.java create mode 100644 pki/base/util/src/netscape/security/util/ByteToCharUniversalString.java create mode 100644 pki/base/util/src/netscape/security/util/CertPrettyPrint.java create mode 100644 pki/base/util/src/netscape/security/util/CharToByteIA5String.java create mode 100644 pki/base/util/src/netscape/security/util/CharToBytePrintable.java create mode 100644 pki/base/util/src/netscape/security/util/CharToByteUniversalString.java create mode 100644 pki/base/util/src/netscape/security/util/CrlPrettyPrint.java create mode 100644 pki/base/util/src/netscape/security/util/DerEncoder.java create mode 100644 pki/base/util/src/netscape/security/util/DerInputBuffer.java create mode 100644 pki/base/util/src/netscape/security/util/DerInputStream.java create mode 100644 pki/base/util/src/netscape/security/util/DerOutputStream.java create mode 100644 pki/base/util/src/netscape/security/util/DerValue.java create mode 100644 pki/base/util/src/netscape/security/util/ExtPrettyPrint.java create mode 100644 pki/base/util/src/netscape/security/util/ObjectIdentifier.java create mode 100644 pki/base/util/src/netscape/security/util/PrettyPrintFormat.java create mode 100644 pki/base/util/src/netscape/security/util/PrettyPrintResources.java create mode 100644 pki/base/util/src/netscape/security/util/PubKeyPrettyPrint.java (limited to 'pki/base/util/src/netscape/security/util') diff --git a/pki/base/util/src/netscape/security/util/ASN1CharStrConvMap.java b/pki/base/util/src/netscape/security/util/ASN1CharStrConvMap.java new file mode 100644 index 000000000..cacde77c8 --- /dev/null +++ b/pki/base/util/src/netscape/security/util/ASN1CharStrConvMap.java @@ -0,0 +1,225 @@ +// --- 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.util; + +import java.util.*; +import sun.io.*; + +/** + * Maps a ASN.1 character string type to a CharToByte and ByteToChar converter. + * The converter is used to convert a DerValue of a ASN.1 character string type + * from bytes to unicode characters and vice versa. + * + *

A global default ASN1CharStrConvMap is created when the class is + * initialized. The global default map is extensible. + * + * @author Lily Hsiao + * @author Slava Galperin + * + */ + +public class ASN1CharStrConvMap +{ + // public constructors + + /** + * Constructs a ASN1CharStrConvMap. + */ + public ASN1CharStrConvMap() + { + } + + /** + * Get a Character to Byte converter for the specified DER tag. + * + * @param tag A DER tag of a ASN.1 character string type, + * for example DerValue.tag_PrintableString. + * + * @return A CharToByteConverter for the DER tag. + * + * @exception InstantiationException + * if error occurs when instantiating the CharToByteConverter. + * @exception IllegalAccessException + * if error occurs when loading the CharToByteConverter class. + */ + public CharToByteConverter getCBC(byte tag) + throws IllegalAccessException, InstantiationException + { + Byte tagObj = Byte.valueOf(tag); + CharToByteConverter cbc = null; + Class cbcClass; + cbcClass = (Class)tag2CBC.get(tagObj); + if (cbcClass == null) + return null; + cbc = (CharToByteConverter)cbcClass.newInstance(); + cbc.setSubstitutionMode(false); + return cbc; + } + + /** + * Get a Byte to Character converter for the given DER tag. + * + * @param tag A DER tag of a ASN.1 character string type, + * for example DerValue.tag_PrintableString. + * + * @return A ByteToCharConverter for the DER tag. + * + * @exception InstantiationException + * if error occurs when instantiationg the ByteToCharConverter. + * @exception IllegalAccessException + * if error occurs when loading the ByteToCharConverter class. + */ + public ByteToCharConverter getBCC(byte tag) + throws IllegalAccessException, InstantiationException + { + Byte tagObj = Byte.valueOf(tag); + ByteToCharConverter bcc = null; + Class bccClass = (Class)tag2BCC.get(tagObj); + if (bccClass == null) + return null; + bcc = (ByteToCharConverter)bccClass.newInstance(); + bcc.setSubstitutionMode(false); + return bcc; + } + + /** + * Add a tag-CharToByteConverter-ByteToCharConverter entry in the map. + * + * @param tag A DER tag of a ASN.1 character string type, + * ex. DerValue.tag_IA5String + * @param cbc A CharToByteConverter for the tag. + * @param bcc A ByteToCharConverter for the tag. + */ + public void addEntry(byte tag, Class cbc, Class bcc) + { + Class current_cbc; + Class current_bcc; + Byte tagByte = Byte.valueOf(tag); + + current_cbc = (Class)tag2CBC.get(tagByte); + current_bcc = (Class)tag2BCC.get(tagByte); + if (current_cbc != null || current_bcc != null) + { + if (current_cbc != cbc || current_bcc != bcc) + { + throw new IllegalArgumentException( + "a DER tag to converter entry already exists."); + } + else { + return; + } + } + if (!CharToByteConverter.class.isAssignableFrom(cbc) || + !ByteToCharConverter.class.isAssignableFrom(bcc)) { + throw new IllegalArgumentException( + "arguments not a CharToByteConverter or ByteToCharConverter"); + } + tag2CBC.put(tagByte, cbc); + tag2BCC.put(tagByte, bcc); + } + + /** + * Get and enumeration of all tags in the map. + * @return An Enumeration of DER tags in the map as Bytes. + */ + public Enumeration getTags() + { + return tag2CBC.keys(); + } + + // static public methods. + + /** + * Get the global ASN1CharStrConvMap. + * @return The global default ASN1CharStrConvMap. + */ + static public ASN1CharStrConvMap getDefault() + { + return defaultMap; + } + + /** + * Set the global default ASN1CharStrConvMap. + * @param newDefault The new default ASN1CharStrConvMap. + */ + static public void setDefault(ASN1CharStrConvMap newDefault) + { + if (newDefault == null) + throw new IllegalArgumentException( + "Cannot set a null default Der Tag Converter map"); + defaultMap = newDefault; + } + + // private methods and variables. + + private Hashtable tag2CBC = new Hashtable(); + private Hashtable tag2BCC = new Hashtable(); + + private static ASN1CharStrConvMap defaultMap; + + /** + * Create the default converter map on initialization + */ + static { + defaultMap = new ASN1CharStrConvMap(); + defaultMap.addEntry(DerValue.tag_PrintableString, + CharToBytePrintable.class, ByteToCharPrintable.class); + defaultMap.addEntry(DerValue.tag_VisibleString, + CharToBytePrintable.class, ByteToCharPrintable.class); + defaultMap.addEntry(DerValue.tag_IA5String, + CharToByteIA5String.class, ByteToCharIA5String.class); + defaultMap.addEntry(DerValue.tag_BMPString, + // Changed by bskim + //sun.io.CharToByteUnicode.class, + //netscape.security.util.ByteToCharUnicode.class); + sun.io.CharToByteUnicodeBig.class, + sun.io.ByteToCharUnicodeBig.class); + // Change end + defaultMap.addEntry(DerValue.tag_UniversalString, + CharToByteUniversalString.class, + ByteToCharUniversalString.class); + // XXX this is an oversimplified implementation of T.61 strings, it + // doesn't handle all cases + defaultMap.addEntry(DerValue.tag_T61String, + latin1CBC.class, latin1BCC.class); + // UTF8String added to ASN.1 in 1998 + defaultMap.addEntry(DerValue.tag_UTF8String, + CharToByteUTF8.class, + ByteToCharUTF8.class); + defaultMap.addEntry(DerValue.tag_GeneralString, + CharToByteUTF8.class, + ByteToCharUTF8.class); + }; + +}; + +class latin1CBC extends sun.io.CharToByteISO8859_1 { + public latin1CBC() { + super(); + subMode = false; + } +} + +class latin1BCC extends sun.io.ByteToCharISO8859_1 { + public latin1BCC() { + super(); + subMode = false; + } +} + + diff --git a/pki/base/util/src/netscape/security/util/ArraySet.java b/pki/base/util/src/netscape/security/util/ArraySet.java new file mode 100644 index 000000000..f6f43efd4 --- /dev/null +++ b/pki/base/util/src/netscape/security/util/ArraySet.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.util; + +import java.util.*; + +/** + * This class implements the Set interface, backed by an ArrayList. It is + * designed to offer good performance for very small Sets, especially those + * that are frequently created and destroyed. The performance will be + * extremely bad for large Sets: ArraySet provides linear time + * performance for the basic operations (add, remove and contains). This Set + * permit all elements, including null. + *

+ *

+ * Note that this implementation is not synchronized. If + * multiple threads access an ArraySet concurrently, and at least one of the + * threads modifies the ArraySet, it must be synchronized externally. + * This is typically accomplished by synchronizing on some object that + * naturally encapsulates the ArraySet. If no such object exists, the ArraySet + * should be "wrapped" using the Collections.synchronizedSet method. This is + * best done at creation time, to prevent accidental unsynchronized access to + * the ArraySet: + *

+ *	Set s = Collections.synchronizedSet(new ArraySet(...));
+ * 
+ *

+ * The Iterators returned by ArraySet's iterator method are + * fail-fast: if the HashSet is modified at any time after the + * Iterator is created, in any way except through the Iterator's own remove + * method, the Iterator will throw a ConcurrentModificationException. Thus, + * in the face of concurrent modification, the Iterator fails quickly and + * cleanly, rather than risking arbitrary, non-deterministic behavior at an + * undetermined time in the future. + * + * @author Josh Bloch + * @version 1.7 10/13/97 + * @see Collection + * @see Set + * @see HashSet + * @see ArrayList + * @since JDK1.2 + */ + +public class ArraySet extends AbstractSet + implements Set, Cloneable, java.io.Serializable { + private ArrayList a; + + /** + * Constructs a new, empty ArraySet; the backing ArrayList has default + * initial capacity and capacity increment. + * + * @since JDK1.2 + */ + public ArraySet() { + a = new ArrayList(); + } + + /** + * Constructs a new ArraySet containing the elements in the specified + * Collection. The backing ArrayList has default initial capacity and + * capacity increment. + * + * @exception NullPointerException the specified collection is null. + * @since JDK1.2 + */ + public ArraySet(Collection c) { + a = new ArrayList(); + + // Add elements of c to a. Don't check for dups if c is a Set. + Iterator i = c.iterator(); + if(c instanceof Set) { + while(i.hasNext()) + a.add(i.next()); + } else { + while(i.hasNext()) + add(i.next()); + } + } + + /** + * Constructs a new, empty ArraySet; the backing ArrayList has the + * specified initial capacity and default capacity increment. + * + * @param initialCapacity the initial capacity of the ArrayList. + * @since JDK1.2 + */ + public ArraySet(int initialCapacity) { + a = new ArrayList(initialCapacity); + } + + /** + * Returns an Iterator over the elements in this ArraySet. The elements + * are returned in the order they were added. + * + * @since JDK1.2 + */ + public Iterator iterator() { + return a.iterator(); + } + + /** + * Returns the number of elements in this ArraySet (its cardinality). + * + * @since JDK1.2 + */ + public int size() { + return a.size(); + } + + /** + * Returns true if this ArraySet contains no elements. + * + * @since JDK1.2 + */ + public boolean isEmpty() { + return a.size()==0; + } + + /** + * Returns true if this ArraySet contains the specified element. + * + * @since JDK1.2 + */ + public boolean contains(Object o) { + return a.contains(o); + } + + /** + * Adds the specified element to this Set if it is not already present. + * + * @param o element to be added to this Set. + * @return true if the Set did not already contain the specified element. + * @since JDK1.2 + */ + public boolean add(Object o) { + boolean modified; + if (modified = !a.contains(o)) + a.add(o); + return modified; + } + + /** + * Removes the given element from this Set if it is present. + * + * @param o object to be removed from this Set, if present. + * @return true if the Set contained the specified element. + * @since JDK1.2 + */ + public boolean remove(Object o) { + return a.remove(o); + } + + /** + * Removes all of the elements from this Set. + * + * @since JDK1.2 + */ + public void clear() { + a.clear(); + } + + /** + * Returns a shallow copy of this ArraySet. (The elements themselves + * are not cloned.) + * + * @since JDK1.2 + */ + public Object clone() { + try { + ArraySet newSet = (ArraySet)super.clone(); + newSet.a = (ArrayList)a.clone(); + return newSet; + } catch (CloneNotSupportedException e) { + throw new InternalError(); + } + } +} diff --git a/pki/base/util/src/netscape/security/util/BigInt.java b/pki/base/util/src/netscape/security/util/BigInt.java new file mode 100644 index 000000000..10e4569de --- /dev/null +++ b/pki/base/util/src/netscape/security/util/BigInt.java @@ -0,0 +1,203 @@ +// --- 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.util; + +import java.math.BigInteger; + + +/** + * A low-overhead arbitrary-precision unsigned integer. + * This is intended for use with ASN.1 parsing, and printing of + * such parsed values. Convert to "BigInteger" if you need to do + * arbitrary precision arithmetic, rather than just represent + * the number as a wrapped array of bytes. + * + *

NOTE: This class may eventually disappear, to + * be supplanted by big-endian byte arrays which hold both signed + * and unsigned arbitrary-precision integers. + * + * @version 1.23 + * @author David Brownell + */ +public final class BigInt { + + // Big endian -- MSB first. + private byte[] places; + + /** + * Constructs a "Big" integer from a set of (big-endian) bytes. + * Leading zeroes should be stripped off. + * + * @param data a sequence of bytes, most significant bytes/digits + * first. CONSUMED. + */ + public BigInt(byte[] data) { places = (byte[])data.clone(); } + + /** + * Constructs a "Big" integer from a "BigInteger", which must be + * positive (or zero) in value. + */ + public BigInt(BigInteger i) { + byte[] temp = i.toByteArray(); + + if ((temp [0] & 0x80) != 0) + throw new IllegalArgumentException ("negative BigInteger"); + + // XXX we assume exactly _one_ sign byte is used... + + if (temp [0] != 0) + places = temp; + else { + // Note that if i = new BigInteger("0"), + // i.toByteArray() contains only 1 zero. + if (temp.length == 1) { + places = new byte [1]; + places [0] = (byte) 0; + } else { + places = new byte [temp.length - 1]; + for (int j = 1; j < temp.length; j++) + places [j - 1] = temp [j]; + } + } + } + + /** + * Constructs a "Big" integer from a normal Java integer. + * + * @param i the java primitive integer + */ + public BigInt(int i) { + if (i < (1 << 8)) { + places = new byte [1]; + places [0] = (byte) i; + } else if (i < (1 << 16)) { + places = new byte [2]; + places[0] = (byte) (i >> 8); + places[1] = (byte) i; + } else if (i < (1 << 24)) { + places = new byte [3]; + places[0] = (byte) (i >> 16); + places [1] = (byte) (i >> 8); + places[2] = (byte) i; + } else { + places = new byte [4]; + places[0] = (byte) (i >> 24); + places[1] = (byte) (i >> 16); + places[2] = (byte) (i >> 8); + places[3] = (byte) i; + } + } + + /** + * Converts the "big" integer to a java primitive integer. + * + * @exception NumberFormatException if 32 bits is insufficient. + */ + public int toInt() { + if (places.length > 4) + throw new NumberFormatException ("BigInt.toInt, too big"); + int retval = 0, i = 0; + for (; i < places.length; i++) + retval = (retval << 8) + ((int)places[i] & 0xff); + return retval; + } + + /** + * Returns a hexadecimal printed representation. The value is + * formatted to fit on lines of at least 75 characters, with + * embedded newlines. Words are separated for readability, + * with eight words (32 bytes) per line. + */ + public String toString() { return hexify(); } + + /** + * Returns a BigInteger value which supports many arithmetic + * operations. Assumes negative values will never occur. + */ + public BigInteger toBigInteger () + { return new BigInteger(1, places); } + + /** + * Returns the length of the data as a byte array. + */ + public int byteLength() { return places.length; } + + + /** + * Returns the data as a byte array. The most significant bit + * of the array is bit zero (as in java.math.BigInteger). + */ + public byte [] toByteArray () { + if (places.length == 0) { + byte zero[] = new byte [1]; + zero [0] = (byte) 0; + return zero; + } else { + return (byte [])places.clone(); + } + } + + private static final String digits = "0123456789abcdef"; + private String hexify() { + if (places.length == 0) + return " 0 "; + + StringBuffer buf = new StringBuffer (places.length * 2); + buf.append (" "); // four spaces + for (int i = 0; i < places.length; i++) { + buf.append (digits.charAt ((places [i] >> 4) & 0x0f)); + buf.append (digits.charAt (places [i] & 0x0f)); + if (((i + 1) % 32) == 0) { + if ((i + 1) != places.length) + buf.append ("\n "); // line after four words + } else if (((i + 1) % 4) == 0) + buf.append (' '); // space between words + } + return buf.toString (); + } + + /** + * Returns true iff the parameter is a numerically equivalent + * BigInt. + * + * @param other the object being compared with this one. + */ + public boolean equals(Object other) { + if (other instanceof BigInt) + return equals ((BigInt) other); + return false; + } + + /** + * Returns true iff the parameter is numerically equivalent. + * + * @param other the BigInt being compared with this one. + */ + public boolean equals(BigInt other) { + if (this == other) + return true; + + byte[] otherPlaces = other.toByteArray(); + if (places.length != otherPlaces.length) + return false; + for (int i = 0; i < places.length; i++) + if (places[i] != otherPlaces[i]) + return false; + return true; + } +} diff --git a/pki/base/util/src/netscape/security/util/BitArray.java b/pki/base/util/src/netscape/security/util/BitArray.java new file mode 100644 index 000000000..43af482d5 --- /dev/null +++ b/pki/base/util/src/netscape/security/util/BitArray.java @@ -0,0 +1,258 @@ +// --- 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.util; + +import java.io.ByteArrayOutputStream; + +/** + * A packed array of booleans. + * + * @author Joshua Bloch + * @author Douglas Hoover + * @version 1.2 97/12/10 + */ + +public class BitArray { + + private byte[] repn; + private int length; + + private static final int BITS_PER_UNIT = 8; + + private static int subscript(int idx) { + return idx / BITS_PER_UNIT; + } + + private static int position(int idx) { // bits big-endian in each unit + return 1 << (BITS_PER_UNIT - 1 - (idx % BITS_PER_UNIT)); + } + + /** + * Creates a BitArray of the specified size, initialized to zeros. + */ + public BitArray(int length) throws IllegalArgumentException { + if (length < 0) { + throw new IllegalArgumentException("Negative length for BitArray"); + } + + this.length = length; + + repn = new byte[(length + BITS_PER_UNIT - 1)/BITS_PER_UNIT]; + } + + + /** + * Creates a BitArray of the specified size, initialized from the + * specified byte array. The most significant bit of a[0] gets + * index zero in the BitArray. The array a must be large enough + * to specify a value for every bit in the BitArray. In other words, + * 8*a.length >= length. + */ + public BitArray(int length, byte[] a) throws IllegalArgumentException { + + if (length < 0) { + throw new IllegalArgumentException("Negative length for BitArray"); + } + if (a.length * BITS_PER_UNIT < length) { + throw new IllegalArgumentException("Byte array too short to represent " + + "bit array of given length"); + } + + this.length = length; + + int repLength = ((length + BITS_PER_UNIT - 1)/BITS_PER_UNIT); + int unusedBits = repLength*BITS_PER_UNIT - length; + byte bitMask = (byte) (0xFF << unusedBits); + + /* + normalize the representation: + 1. discard extra bytes + 2. zero out extra bits in the last byte + */ + repn = new byte[repLength]; + System.arraycopy(a, 0, repn, 0, repLength); + if (repn.length > 0) + repn[repn.length -1] = (byte) (repn[repn.length -1] & bitMask); + } + + /** + * Create a BitArray whose bits are those of the given array + * of Booleans. + */ + public BitArray(boolean[] bits) { + length = bits.length; + repn = new byte[(length + 7)/8]; + + for (int i=0; i < length; i++) { + set(i, bits[i]); + } + } + + + /** + * Copy constructor (for cloning). + */ + private BitArray(BitArray ba) { + length = ba.length; + repn = (byte[]) ba.repn.clone(); + } + + /** + * Returns the indexed bit in this BitArray. + */ + public boolean get(int index) throws ArrayIndexOutOfBoundsException { + if (index < 0 || index >= length) { + throw new ArrayIndexOutOfBoundsException(Integer.toString(index)); + } + + return (repn[subscript(index)] & position(index)) != 0; + } + + /** + * Sets the indexed bit in this BitArray. + */ + public void set(int index, boolean value) + throws ArrayIndexOutOfBoundsException { + if (index < 0 || index >= length) { + throw new ArrayIndexOutOfBoundsException(Integer.toString(index)); + } + int idx = subscript(index); + int bit = position(index); + + if (value) { + repn[idx] |= bit; + } else { + repn[idx] &= ~bit; + } + } + + /** + * Returns the length of this BitArray. + */ + public int length() { + return length; + } + + /** + * Returns a Byte array containing the contents of this BitArray. + * The bit stored at index zero in this BitArray will be copied + * into the most significant bit of the zeroth element of the + * returned byte array. The last byte of the returned byte array + * will be contain zeros in any bits that do not have corresponding + * bits in the BitArray. (This matters only if the BitArray's size + * is not a multiple of 8.) + */ + public byte[] toByteArray() { + return (byte[]) repn.clone(); + } + + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || !(obj instanceof BitArray)) return false; + + BitArray ba = (BitArray) obj; + + if (ba.length != length) return false; + + for (int i = 0; i < repn.length; i += 1) { + if (repn[i] != ba.repn[i]) return false; + } + return true; + } + + /** + * Return a boolean array with the same bit values a this BitArray. + */ + public boolean[] toBooleanArray() { + boolean[] bits = new boolean[length]; + + for (int i=0; i < length; i++) { + bits[i] = get(i); + } + return bits; + } + + /** + * Returns a hash code value for this bit array. + * + * @return a hash code value for this bit array. + */ + public int hashCode() { + int hashCode = 0; + + for (int i = 0; i < repn.length; i++) + hashCode = 31*hashCode + repn[i]; + + return hashCode ^ length; + } + + + public Object clone() { + return new BitArray(this); + } + + + private static final byte[][] NYBBLE = { + { (byte)'0',(byte)'0',(byte)'0',(byte)'0'}, + { (byte)'0',(byte)'0',(byte)'0',(byte)'1'}, + { (byte)'0',(byte)'0',(byte)'1',(byte)'0'}, + { (byte)'0',(byte)'0',(byte)'1',(byte)'1'}, + { (byte)'0',(byte)'1',(byte)'0',(byte)'0'}, + { (byte)'0',(byte)'1',(byte)'0',(byte)'1'}, + { (byte)'0',(byte)'1',(byte)'1',(byte)'0'}, + { (byte)'0',(byte)'1',(byte)'1',(byte)'1'}, + { (byte)'1',(byte)'0',(byte)'0',(byte)'0'}, + { (byte)'1',(byte)'0',(byte)'0',(byte)'1'}, + { (byte)'1',(byte)'0',(byte)'1',(byte)'0'}, + { (byte)'1',(byte)'0',(byte)'1',(byte)'1'}, + { (byte)'1',(byte)'1',(byte)'0',(byte)'0'}, + { (byte)'1',(byte)'1',(byte)'0',(byte)'1'}, + { (byte)'1',(byte)'1',(byte)'1',(byte)'0'}, + { (byte)'1',(byte)'1',(byte)'1',(byte)'1'} + }; + + private static final int BYTES_PER_LINE = 8; + + /** + * Returns a string representation of this BitArray. + */ + public String toString() { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + for (int i = 0; i < repn.length - 1; i++) { + out.write(NYBBLE[(repn[i] >> 4) & 0x0F], 0, 4); + out.write(NYBBLE[repn[i] & 0x0F], 0, 4); + + if (i % BYTES_PER_LINE == BYTES_PER_LINE - 1) { + out.write('\n'); + } else { + out.write(' '); + } + } + + // in last byte of repn, use only the valid bits + for (int i = BITS_PER_UNIT * (repn.length - 1); i < length; i++) { + out.write(get(i) ? '1' : '0'); + } + + return new String(out.toByteArray()); + + } + +} + diff --git a/pki/base/util/src/netscape/security/util/ByteArrayLexOrder.java b/pki/base/util/src/netscape/security/util/ByteArrayLexOrder.java new file mode 100644 index 000000000..60aede101 --- /dev/null +++ b/pki/base/util/src/netscape/security/util/ByteArrayLexOrder.java @@ -0,0 +1,63 @@ +// --- 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.util; + +import java.util.Comparator; + +/** + * Compare two byte arrays in lexicographical order. + * + * @version 1.4 97/12/10 + * @author D. N. Hoover + */ +public class ByteArrayLexOrder implements Comparator { + + /** + * Perform lexicographical comparison of two byte arrays, + * regarding each byte as unsigned. That is, compare array entries + * in order until they differ--the array with the smaller entry + * is "smaller". If array entries are + * equal till one array ends, then the longer array is "bigger". + * + * @param obj1 first byte array to compare. + * @param obj2 second byte array to compare. + * @return negative number if obj1 < obj2, 0 if obj1 == obj2, + * positive number if obj1 > obj2. + * + * @exception ClassCastException + * if either argument is not a byte array. + */ + public final int compare(Object obj1, Object obj2) { + + byte[] bytes1 = (byte[]) obj1; + byte[] bytes2 = (byte[]) obj2; + + int diff; + for (int i = 0; i < bytes1.length && i < bytes2.length; i++) { + diff = (bytes1[i] & 0xFF) - (bytes2[i] & 0xFF); + if (diff != 0) { + return diff; + } + } + // if array entries are equal till the first ends, then the + // longer is "bigger" + return bytes1.length - bytes2.length; + } + + +} diff --git a/pki/base/util/src/netscape/security/util/ByteArrayTagOrder.java b/pki/base/util/src/netscape/security/util/ByteArrayTagOrder.java new file mode 100644 index 000000000..81f437476 --- /dev/null +++ b/pki/base/util/src/netscape/security/util/ByteArrayTagOrder.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.util; + +import java.util.Comparator; + +public class ByteArrayTagOrder implements Comparator { + + /** + * Compare two byte arrays, by the order of their tags, + * as defined in ITU-T X.680, sec. 6.4. (First compare + * tag classes, then tag numbers, ignoring the constructivity bit.) + * + * @param obj1 first byte array to compare. + * @param obj2 second byte array to compare. + * @return negative number if obj1 < obj2, 0 if obj1 == obj2, + * positive number if obj1 > obj2. + * + * @exception ClassCastException + * if either argument is not a byte array. + */ + + public final int compare(Object obj1, Object obj2) { + + byte[] bytes1 = (byte[]) obj1; + byte[] bytes2 = (byte[]) obj2; + + // tag order is same as byte order ignoring any difference in + // the constructivity bit (0x02) + return (bytes1[0] | 0x20) - (bytes2[0] | 0x20); + } + + +} diff --git a/pki/base/util/src/netscape/security/util/ByteToCharIA5String.java b/pki/base/util/src/netscape/security/util/ByteToCharIA5String.java new file mode 100644 index 000000000..69fab22a7 --- /dev/null +++ b/pki/base/util/src/netscape/security/util/ByteToCharIA5String.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.util; + +import sun.io.ByteToCharConverter; +import sun.io.ConversionBufferFullException; +import sun.io.UnknownCharacterException; + +/** + * Converts bytes in ASN.1 IA5String character set to unicode + * characters. + * + * @author Lily Hsiao + * @author Slava Galperin + */ + +public class ByteToCharIA5String extends ByteToCharConverter +{ + public String getCharacterEncoding() { + return "ASN.1 IA5String"; + } + + public int convert(byte[] input, int inStart, int inEnd, + char[] output, int outStart, int outEnd) + throws ConversionBufferFullException, + UnknownCharacterException + { + int j = outStart; + for (int i = inStart; i < inEnd; i++, j++) { + if (j >= outEnd) { + byteOff = i; + charOff = j; + throw new ConversionBufferFullException(); + } + if (!subMode && (input[i] & 0x80) != 0) { + byteOff = i; + charOff = j; + badInputLength = 1; + throw new UnknownCharacterException(); + } + output[j] = (char) (input[i] & 0x7f); + } + byteOff = inEnd; + charOff = j; + return j - outStart; + } + + public int flush(char[] output, int outStart, int outEnd) { + return 0; + } + + public void reset() { } + +} diff --git a/pki/base/util/src/netscape/security/util/ByteToCharPrintable.java b/pki/base/util/src/netscape/security/util/ByteToCharPrintable.java new file mode 100644 index 000000000..c42b457d0 --- /dev/null +++ b/pki/base/util/src/netscape/security/util/ByteToCharPrintable.java @@ -0,0 +1,88 @@ +// --- 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.util; + +import sun.io.ByteToCharConverter; +import sun.io.ConversionBufferFullException; +import sun.io.UnknownCharacterException; +import sun.io.MalformedInputException; + +/** + * Converts bytes in ASN.1 Printable String character set to unicode + * characters. + * + * @author Lily Hsiao + * @author Slava Galperin + */ + +public class ByteToCharPrintable extends ByteToCharConverter +{ + + public String getCharacterEncoding() + { + return "ASN.1 Printable"; + } + + public int convert(byte[] input, int inStart, int inEnd, + char[] output, int outStart, int outEnd) + throws MalformedInputException, + UnknownCharacterException, + ConversionBufferFullException + { + int j = outStart; + boolean hasNonPrintableChar = false; + + for (int i = inStart; i < inEnd; i++, j++) { + if (j >= outEnd) { + byteOff = i; + charOff = j; + throw new ConversionBufferFullException(); + } + if (!subMode && + !CharToBytePrintable.isPrintableChar((char) (input[i] & 0x7f))) { + /* "bug" fix for 359010 + byteOff = i; + charOff = j; + badInputLength = 1; + throw new UnknownCharacterException(); + */ + j--; + hasNonPrintableChar = true; + } else + output[j] = (char) (input[i] & 0x7f); + } + + if (hasNonPrintableChar == true) { + // + } + + byteOff = inEnd; + charOff = j; + return j - outStart; + } + + public int flush( char[] output, int outStart, int outEnd ) + throws MalformedInputException, ConversionBufferFullException + { + return 0; + } + + public void reset() { } + + +} diff --git a/pki/base/util/src/netscape/security/util/ByteToCharUnicode.java b/pki/base/util/src/netscape/security/util/ByteToCharUnicode.java new file mode 100644 index 000000000..717081be7 --- /dev/null +++ b/pki/base/util/src/netscape/security/util/ByteToCharUnicode.java @@ -0,0 +1,189 @@ +// --- 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.util; +import java.io.*; + +import sun.io.ByteToCharConverter; +import sun.io.ConversionBufferFullException; +import sun.io.UnknownCharacterException; +import sun.io.MalformedInputException; + +/** + * Convert byte arrays containing Unicode characters into arrays of actual + * Unicode characters, sensing the byte order automatically. To force a + * particular byte order, use either the "UnicodeBig" or the "UnicodeLittle" + * encoding. + * + * If the first character is a byte order mark, it will be interpreted and + * discarded. Otherwise, the byte order is assumed to be BigEndian. + * Either way, the byte order is decided by the first character. Later + * byte order marks will be passed through as characters (if they indicate + * the same byte order) or will cause an error (if they indicate the other + * byte order). + * + * @see ByteToCharUnicodeLittle + * @see ByteToCharUnicodeBig + * + * @version 1.3, 96/11/23 + * @author Mark Reinhold + */ + +public class ByteToCharUnicode extends sun.io.ByteToCharConverter { + + static final char BYTE_ORDER_MARK = (char) 0xfeff; + static final char REVERSED_MARK = (char) 0xfffe; + + static final int AUTO = 0; + static final int BIG = 1; + static final int LITTLE = 2; + + int byteOrder; + + public ByteToCharUnicode() { + byteOrder = AUTO; + } + + public String getCharacterEncoding() { + switch (byteOrder) { + case BIG: return "UnicodeBig"; + case LITTLE: return "UnicodeLittle"; + default: return "Unicode"; + } + } + + boolean started = false; + int leftOverByte; + boolean leftOver = false; + + public int convert(byte[] in, int inOff, int inEnd, + char[] out, int outOff, int outEnd) + throws ConversionBufferFullException, MalformedInputException + { + byteOff = inOff; + charOff = outOff; + + if (inOff >= inEnd) + return 0; + + int b1, b2; + int bc = 0; + int inI = inOff, outI = outOff; + + if (leftOver) { + b1 = leftOverByte & 0xff; + leftOver = false; + } + else + b1 = in[inI++] & 0xff; + bc = 1; + + if (!started) { /* Read possible initial byte-order mark */ + if (inI < inEnd) { + b2 = in[inI++] & 0xff; + bc = 2; + + char c = (char) ((b1 << 8) | b2); + int bo = AUTO; + + if (c == BYTE_ORDER_MARK) + bo = BIG; + else if (c == REVERSED_MARK) + bo = LITTLE; + + if (byteOrder == AUTO) { + if (bo == AUTO) { + bo = BIG; // BigEndian by default + } + byteOrder = bo; + if (inI < inEnd) { + b1 = in[inI++] & 0xff; + bc = 1; + } + } + else if (bo == AUTO) { + inI--; + bc = 1; + } + else if (byteOrder == bo) { + if (inI < inEnd) { + b1 = in[inI++] & 0xff; + bc = 1; + } + } + else { + badInputLength = bc; + throw new + MalformedInputException("Incorrect byte-order mark"); + } + + started = true; + } + } + + /* Loop invariant: (b1 contains the next input byte) && (bc == 1) */ + while (inI < inEnd) { + b2 = in[inI++] & 0xff; + bc = 2; + + char c; + if (byteOrder == BIG) + c = (char) ((b1 << 8) | b2); + else + c = (char) ((b2 << 8) | b1); + + if (c == REVERSED_MARK) + throw new + MalformedInputException("Reversed byte-order mark"); + + if (outI >= outEnd) + throw new ConversionBufferFullException(); + out[outI++] = c; + byteOff = inI; + charOff = outI; + + if (inI < inEnd) { + b1 = in[inI++] & 0xff; + bc = 1; + } + } + + if (bc == 1) { + leftOverByte = b1; + leftOver = true; + } + + return outI - outOff; + } + + public void reset() { + leftOver = false; + byteOff = charOff = 0; + } + + public int flush(char buf[], int off, int len) + throws MalformedInputException + { + if (leftOver) { + reset(); + throw new MalformedInputException(); + } + byteOff = charOff = 0; + return 0; + } + +} diff --git a/pki/base/util/src/netscape/security/util/ByteToCharUniversalString.java b/pki/base/util/src/netscape/security/util/ByteToCharUniversalString.java new file mode 100644 index 000000000..57b678dd8 --- /dev/null +++ b/pki/base/util/src/netscape/security/util/ByteToCharUniversalString.java @@ -0,0 +1,97 @@ +// --- 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.util; + +import sun.io.*; + +/** + * Converts bytes in ASN.1 UniversalString character set to unicode + * characters. + * + * @author Lily Hsiao + * @author Slava Galperin + */ + +public class ByteToCharUniversalString extends ByteToCharConverter +{ + public String getCharacterEncoding() { + return "ASN.1 UniversalString"; + } + + public int convert(byte[] input, int inStart, int inEnd, + char[] output, int outStart, int outEnd) + throws ConversionBufferFullException, + UnknownCharacterException + { + int j = outStart; + + + int i = inStart; + while(i < inEnd) { + // XXX we do not know what to do with truly UCS-4 characters here + // we also assumed network byte order + + if ( i+3 >= inEnd || + (!((input[i] == 0 && input[i+1] == 0) || + (input[i+2] == 0 && input[i+3] == 0)))) { + byteOff = i; + charOff = j; + throw new UnknownCharacterException(); + } + if (input[i+2] == 0 && input[i+3] == 0) { + // Try to be a bit forgiving. If the byte order is + // reversed, we still try handle it. + + // Sample Date Set (1): + // 0000000 f 0 \0 \0 213 0 \0 \0 S 0 \0 \0 + // 0000014 + + // Sample Date Set (2): + // 0000000 w \0 \0 \0 w \0 \0 \0 w \0 \0 \0 . \0 \0 \0 + // 0000020 ( \0 \0 \0 t \0 \0 \0 o \0 \0 \0 b \0 \0 \0 + // 0000040 e \0 \0 \0 | \0 \0 \0 n \0 \0 \0 o \0 \0 \0 + // 0000060 t \0 \0 \0 t \0 \0 \0 o \0 \0 \0 b \0 \0 \0 + // 0000100 e \0 \0 \0 ) \0 \0 \0 . \0 \0 \0 c \0 \0 \0 + // 0000120 o \0 \0 \0 m \0 \0 \0 + // 0000130 + output[j] = (char)(((input[i+1] << 8)& 0xff00) + (input[i] & 0x00ff)); + } else { + // This should be the right order. + // + // 0000000 0000 00c4 0000 0064 0000 006d 0000 0069 + // 0000020 0000 006e 0000 0020 0000 0051 0000 0041 + // 0000040 + + // (input[i] == 0 && input[i+1] == 0) + output[j] = (char)(((input[i+2] << 8)& 0xff00) + (input[i+3] & 0x00ff)); + } + j++; + i += 4; + } + byteOff = inEnd; + charOff = j; + return j - outStart; + } + + public int flush(char[] output, int outStart, int outEnd) { + return 0; + } + + public void reset() { } + +} diff --git a/pki/base/util/src/netscape/security/util/CertPrettyPrint.java b/pki/base/util/src/netscape/security/util/CertPrettyPrint.java new file mode 100644 index 000000000..512866913 --- /dev/null +++ b/pki/base/util/src/netscape/security/util/CertPrettyPrint.java @@ -0,0 +1,338 @@ +// --- 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.util; + + +import java.io.*; +import java.util.*; +import java.text.*; +import java.security.PublicKey; +import java.security.cert.Certificate; +import java.security.MessageDigest; +import netscape.security.util.*; +import netscape.security.x509.*; +import org.mozilla.jss.asn1.*; +import org.mozilla.jss.pkcs7.*; + + +/** + * This class will display the certificate content in predefined + * format. + * + * @author Jack Pan-Chen + * @version $Revision: 14564 $, $Date: 2007-05-01 10:40:13 -0700 (Tue, 01 May 2007) $ + */ +public class CertPrettyPrint +{ + + /*========================================================== + * constants + *==========================================================*/ + private final static String CUSTOM_LOCALE = "Custom"; + + /*========================================================== + * variables + *==========================================================*/ + private X509CertImpl mX509Cert = null; + private Certificate mCert = null; + private PrettyPrintFormat pp = null; + private byte[] mCert_b = null; + + /*========================================================== + * constructors + *==========================================================*/ + + public CertPrettyPrint(Certificate cert) { + if (cert instanceof X509CertImpl) + mX509Cert = (X509CertImpl) cert; + + pp = new PrettyPrintFormat(":"); + } + + public CertPrettyPrint(byte[] certb) { + mCert_b = certb; + pp = new PrettyPrintFormat(":"); + } + + /*========================================================== + * public methods + *==========================================================*/ + + /** + * This method return string representation of the certificate + * in predefined format using specified client local. I18N Support. + * + * @param clientLocale Locale to be used for localization + * @return string representation of the certificate + */ + public String toString(Locale clientLocale) { + + if (mX509Cert != null) + return X509toString(clientLocale); + else if (mCert_b != null) + return pkcs7toString(clientLocale); + else + return null; + } + + public String pkcs7toString(Locale clientLocale) { + String content = ""; + + try { + mX509Cert = new X509CertImpl(mCert_b); + return toString(clientLocale); + } catch (Exception e) { + } + + ContentInfo ci = null; + try { + ci = (ContentInfo) + ASN1Util.decode(ContentInfo.getTemplate(), mCert_b); + } catch (Exception e) { + return ""; + } + + if (ci.getContentType().equals(ContentInfo.SIGNED_DATA)) { + SignedData sd = null; + try { + sd = (SignedData) ci.getInterpretedContent(); + } catch (Exception e) { + return ""; + } + + if (sd.hasCertificates()) { + SET certs = sd.getCertificates(); + + for (int i = 0; i < certs.size(); i++) { + org.mozilla.jss.pkix.cert.Certificate cert = (org.mozilla.jss.pkix.cert.Certificate) certs.elementAt(i); + X509CertImpl certImpl = null; + try { + certImpl = new X509CertImpl( + ASN1Util.encode(cert)); + } catch (Exception e) { + } + + CertPrettyPrint print = new CertPrettyPrint(certImpl); + content += print.toString(Locale.getDefault()); + content += "\n"; + } + + return content; + } + } + + return content; + } + + public String stripCertBrackets(String s) { + if (s == null) { + return s; + } + + if ((s.startsWith("-----BEGIN CERTIFICATE-----")) && + (s.endsWith("-----END CERTIFICATE-----"))) { + return (s.substring(27, (s.length() - 25))); + } + + // To support Thawte's header and footer + if ((s.startsWith("-----BEGIN PKCS #7 SIGNED DATA-----")) && + (s.endsWith("-----END PKCS #7 SIGNED DATA-----"))) { + return (s.substring(35, (s.length() - 33))); + } + + return s; + } + + public String normalizeCertStr(String s) { + String val = ""; + + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == '\n') { + continue; + } else if (s.charAt(i) == '\r') { + continue; + } else if (s.charAt(i) == '"') { + continue; + } else if (s.charAt(i) == ' ') { + continue; + } + val += s.charAt(i); + } + return val; + } + + public String X509toString(Locale clientLocale) { + + //get I18N resources + ResourceBundle resource = ResourceBundle.getBundle( + PrettyPrintResources.class.getName()); + DateFormat dateFormater = DateFormat.getDateTimeInstance( + DateFormat.FULL, DateFormat.FULL, clientLocale); + //get timezone and timezone ID + String tz = " "; + String tzid = " "; + + StringBuffer sb = new StringBuffer(); + + try { + X509CertInfo info = (X509CertInfo) mX509Cert.get( + X509CertImpl.NAME + "." + X509CertImpl.INFO); + String serial2 = mX509Cert.getSerialNumber().toString(16).toUpperCase(); + + //get correct instance of key + PublicKey pKey = mX509Cert.getPublicKey(); + X509Key key = null; + + if (pKey instanceof CertificateX509Key) { + CertificateX509Key certKey = (CertificateX509Key) pKey; + + key = (X509Key) certKey.get(CertificateX509Key.KEY); + } + if (pKey instanceof X509Key) { + key = (X509Key) pKey; + } + + //take care of spki + sb.append(pp.indent(4) + resource.getString( + PrettyPrintResources.TOKEN_CERTIFICATE) + "\n"); + sb.append(pp.indent(8) + resource.getString( + PrettyPrintResources.TOKEN_DATA) + "\n"); + sb.append(pp.indent(12) + resource.getString( + PrettyPrintResources.TOKEN_VERSION) + " v"); + sb.append((mX509Cert.getVersion() + 1) + "\n"); + sb.append(pp.indent(12) + resource.getString( + PrettyPrintResources.TOKEN_SERIAL) + "0x" + serial2 + "\n"); + //XXX I18N Algorithm Name ? + sb.append(pp.indent(12) + resource.getString( + PrettyPrintResources.TOKEN_SIGALG) + mX509Cert.getSigAlgName() + + " - " + mX509Cert.getSigAlgOID() + "\n"); + //XXX I18N IssuerDN ? + sb.append(pp.indent(12) + resource.getString( + PrettyPrintResources.TOKEN_ISSUER) + + mX509Cert.getIssuerDN().toString() + "\n"); + sb.append(pp.indent(12) + resource.getString( + PrettyPrintResources.TOKEN_VALIDITY) + "\n"); + String notBefore = dateFormater.format(mX509Cert.getNotBefore()); + String notAfter = dateFormater.format(mX509Cert.getNotAfter()); + + //get timezone and timezone ID + if (TimeZone.getDefault() != null) { + tz = TimeZone.getDefault().getDisplayName( + TimeZone.getDefault().inDaylightTime( + mX509Cert.getNotBefore()), + TimeZone.SHORT, + clientLocale); + tzid = TimeZone.getDefault().getID(); + } + // Specify notBefore + if (tz.equals(tzid) || tzid.equals(CUSTOM_LOCALE)) { + // Do NOT append timezone ID + sb.append(pp.indent(16) + + resource.getString( + PrettyPrintResources.TOKEN_NOT_BEFORE) + + notBefore + + "\n"); + } else { + // Append timezone ID + sb.append(pp.indent(16) + + resource.getString( + PrettyPrintResources.TOKEN_NOT_BEFORE) + + notBefore + + " " + tzid + "\n"); + } + // re-get timezone (just in case it is different . . .) + if (TimeZone.getDefault() != null) { + tz = TimeZone.getDefault().getDisplayName( + TimeZone.getDefault().inDaylightTime( + mX509Cert.getNotAfter()), + TimeZone.SHORT, + clientLocale); + } + // Specify notAfter + if (tz.equals(tzid) || tzid.equals(CUSTOM_LOCALE)) { + // Do NOT append timezone ID + sb.append(pp.indent(16) + + resource.getString( + PrettyPrintResources.TOKEN_NOT_AFTER) + + notAfter + + "\n"); + } else { + // Append timezone ID + sb.append(pp.indent(16) + + resource.getString( + PrettyPrintResources.TOKEN_NOT_AFTER) + + notAfter + + " " + tzid + "\n"); + } + //XXX I18N SubjectDN ? + sb.append(pp.indent(12) + resource.getString( + PrettyPrintResources.TOKEN_SUBJECT) + + mX509Cert.getSubjectDN().toString() + "\n"); + sb.append(pp.indent(12) + resource.getString( + PrettyPrintResources.TOKEN_SPKI) + "\n"); + + PubKeyPrettyPrint pkpp = new PubKeyPrettyPrint(key); + + sb.append(pkpp.toString(clientLocale, 16, 16)); + + //take care of extensions + CertificateExtensions extensions = (CertificateExtensions) + info.get(X509CertInfo.EXTENSIONS); + + sb.append(pp.indent(12) + resource.getString( + PrettyPrintResources.TOKEN_EXTENSIONS) + "\n"); + if (extensions != null) + for (int i = 0; i < extensions.size(); i++) { + Extension ext = (Extension) extensions.elementAt(i); + ExtPrettyPrint extpp = new ExtPrettyPrint(ext, 16); + + sb.append(extpp.toString()); + } + + //take care of signature + sb.append(pp.indent(8) + resource.getString( + PrettyPrintResources.TOKEN_SIGNATURE) + "\n"); + //XXX I18N Algorithm Name ? + sb.append(pp.indent(12) + resource.getString( + PrettyPrintResources.TOKEN_ALGORITHM) + + mX509Cert.getSigAlgName() + " - " + mX509Cert.getSigAlgOID() + "\n"); + sb.append(pp.indent(12) + resource.getString( + PrettyPrintResources.TOKEN_SIGNATURE) + "\n"); + sb.append(pp.toHexString(mX509Cert.getSignature(), 16, 16)); + + // fingerprints + String[] hashes = new String[] {"MD2", "MD5", "SHA1", "SHA256", "SHA512"}; + String certFingerprints = ""; + + sb.append(pp.indent(8) + "FingerPrint\n"); + for (int i = 0; i < hashes.length; i++) { + MessageDigest md = MessageDigest.getInstance(hashes[i]); + + md.update(mX509Cert.getEncoded()); + certFingerprints += pp.indent(12) + hashes[i] + ":\n" + + pp.toHexString(md.digest(), 16, 16); + } + + sb.append(certFingerprints); + } catch (Exception e) { + } + + return sb.toString(); + } + +} diff --git a/pki/base/util/src/netscape/security/util/CharToByteIA5String.java b/pki/base/util/src/netscape/security/util/CharToByteIA5String.java new file mode 100644 index 000000000..f7c0d1e2d --- /dev/null +++ b/pki/base/util/src/netscape/security/util/CharToByteIA5String.java @@ -0,0 +1,88 @@ +// --- 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.util; + +import sun.io.CharToByteConverter; +import sun.io.ConversionBufferFullException; +import sun.io.UnknownCharacterException; + +/** + * Converts a string of ASN.1 IA5String characters to IA5String bytes. + * + * @author Lily Hsiao + * @author Slava Galperin + */ + +public class CharToByteIA5String extends CharToByteConverter +{ + /* + * Returns the character set id for the conversion. + * @return the character set id. + */ + public String getCharacterEncoding () { + return "ASN.1 IA5String"; + } + + /* + * Converts an array of Unicode characters into an array of IA5String + * bytes and returns the total number of characters converted. + * If conversion cannot be done, UnknownCharacterException is + * thrown. The character and byte offset will be set to the point + * of the unknown character. + * @param input character array to convert. + * @param inStart offset from which to start the conversion. + * @param inEnd where to end the conversion. + * @param output byte array to store converted bytes. + * @param outStart starting offset in the output byte array. + * @param outEnd ending offset in the output byte array. + * @return the number of characters converted. + */ + public int convert(char[] input, int inStart, int inEnd, + byte[] output, int outStart, int outEnd) + throws ConversionBufferFullException, + UnknownCharacterException + { + int j = outStart; + for (int i = inStart; i < inEnd; i++, j++) { + if (j >= outEnd) { + charOff = i; + byteOff = j; + throw new ConversionBufferFullException(); + } + if (!subMode && (input[i] & 0xFF80) != 0) { + charOff = i; + byteOff = j; + badInputLength = 1; + throw new UnknownCharacterException(); + } + + output[j] = (byte) (input[i] & 0x7f); + } + return j - outStart; + } + + public int flush(byte[] output, int outStart, int outEnd) { + return 0; + } + + public void reset() { } + + public int getMaxBytesPerChar() { + return 1; + } +} diff --git a/pki/base/util/src/netscape/security/util/CharToBytePrintable.java b/pki/base/util/src/netscape/security/util/CharToBytePrintable.java new file mode 100644 index 000000000..09b3afefe --- /dev/null +++ b/pki/base/util/src/netscape/security/util/CharToBytePrintable.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.util; + +import sun.io.CharToByteConverter; +import sun.io.ConversionBufferFullException; +import sun.io.UnknownCharacterException; +import sun.io.MalformedInputException; + +/** + * Converts a string of ASN.1 PrintableString characters to PrintableString + * bytes. + * + * @author Lily Hsiao + * @author Slava Galperin + */ + +public class CharToBytePrintable extends CharToByteConverter +{ + /* + * returns the character set id for the conversion. + * @return the character set id. + */ + public String getCharacterEncoding() + { + return "ASN.1 Printable"; + } + + public static boolean isPrintableChar( char c ) + { + if ((c < 'A' || c > 'Z') && + (c < 'a' || c > 'z') && + (c < '0' || c > '9') && + (c != ' ') && + (c != '\'') && + (c != '(') && + (c != ')') && + (c != '+') && + (c != ',') && + (c != '-') && + (c != '.') && + (c != '/') && + (c != ':') && + (c != '=') && + (c != '?')) + { + return false; + } else { + return true; + } + } + + /* + * Converts an array of Unicode characters into an array of Printable + * String bytes and returns the total number of characters converted. + * If conversion cannot be done, UnknownCharacterException is + * thrown. The character and byte offset will be set to the point + * of the unknown character. + * @param input character array to convert. + * @param inStart offset from which to start the conversion. + * @param inEnd where to end the conversion. + * @param output byte array to store converted bytes. + * @param outStart starting offset in the output byte array. + * @param outEnd ending offset in the output byte array. + * @return the number of characters converted. + */ + public int convert(char[] input, int inStart, int inEnd, + byte[] output, int outStart, int outEnd) + throws MalformedInputException, UnknownCharacterException, + ConversionBufferFullException + { + int j = outStart; + int i; + for (i = inStart; i < inEnd ; i++, j++) + { + if (j >= outEnd) { + charOff = i; + byteOff = j; + throw new ConversionBufferFullException(); + } + if (!subMode && !isPrintableChar(input[i])) { + charOff = i; + byteOff = j; + badInputLength = 1; + throw new UnknownCharacterException(); + } + output[j] = (byte) (input[i] & 0x7f); + } + charOff = i; + byteOff = j; + return j - outStart; + } + + public int flush(byte[] output, int outStart, int outEnd) + throws MalformedInputException, ConversionBufferFullException + { + return 0; + } + + public void reset() { } + + public int getMaxBytesPerChar() + { + return 1; + } + +} diff --git a/pki/base/util/src/netscape/security/util/CharToByteUniversalString.java b/pki/base/util/src/netscape/security/util/CharToByteUniversalString.java new file mode 100644 index 000000000..0d566d539 --- /dev/null +++ b/pki/base/util/src/netscape/security/util/CharToByteUniversalString.java @@ -0,0 +1,85 @@ +// --- 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.util; + +import sun.io.CharToByteConverter; +import sun.io.ConversionBufferFullException; +import sun.io.UnknownCharacterException; + +/** + * Converts a string of ASN.1 IA5String characters to IA5String bytes. + * + * @author Lily Hsiao + * @author Slava Galperin + */ + +public class CharToByteUniversalString extends CharToByteConverter +{ + /* + * Returns the character set id for the conversion. + * @return the character set id. + */ + public String getCharacterEncoding () { + return "ASN.1 UniversalString"; + } + + /* + * Converts an array of Unicode characters into an array of UniversalString + * bytes and returns the total number of characters converted. + * If conversion cannot be done, UnknownCharacterException is + * thrown. The character and byte offset will be set to the point + * of the unknown character. + * @param input character array to convert. + * @param inStart offset from which to start the conversion. + * @param inEnd where to end the conversion. + * @param output byte array to store converted bytes. + * @param outStart starting offset in the output byte array. + * @param outEnd ending offset in the output byte array. + * @return the number of characters converted. + */ + public int convert(char[] input, int inStart, int inEnd, + byte[] output, int outStart, int outEnd) + throws ConversionBufferFullException, + UnknownCharacterException + { + int j = outStart; + for (int i = inStart; i < inEnd; i++) { + if (j+3 >= outEnd) { + charOff = i; + byteOff = j; + throw new ConversionBufferFullException(); + } + output[j++] = 0; + output[j++] = 0; + output[j++] = (byte) ((input[i] >> 8) & 0xff); + output[j++] = (byte) (input[i] & 0xff); + } + + return j - outStart; + } + + public int flush(byte[] output, int outStart, int outEnd) { + return 0; + } + + public void reset() { } + + public int getMaxBytesPerChar() { + return 4; + } +} diff --git a/pki/base/util/src/netscape/security/util/CrlPrettyPrint.java b/pki/base/util/src/netscape/security/util/CrlPrettyPrint.java new file mode 100644 index 000000000..2795ef7fa --- /dev/null +++ b/pki/base/util/src/netscape/security/util/CrlPrettyPrint.java @@ -0,0 +1,270 @@ +// --- 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.util; + + +import java.io.*; +import java.util.*; +import java.text.*; +import netscape.security.util.*; +import netscape.security.x509.*; +import java.security.*; + + +/** + * This class will display the certificate content in predefined + * format. + * + * @author Andrew Wnuk + * @version $Revision: 14564 $, $Date: 2007-05-01 10:40:13 -0700 (Tue, 01 May 2007) $ + */ +public class CrlPrettyPrint +{ + + /*========================================================== + * constants + *==========================================================*/ + private final static String CUSTOM_LOCALE = "Custom"; + + /*========================================================== + * variables + *==========================================================*/ + private X509CRLImpl mCRL = null; + private PrettyPrintFormat pp = null; + + /*========================================================== + * constructors + *==========================================================*/ + + public CrlPrettyPrint(X509CRLImpl crl) { + mCRL = crl; + pp = new PrettyPrintFormat(":"); + } + + /*========================================================== + * public methods + *==========================================================*/ + + /** + * This method return string representation of the certificate + * revocation list in predefined format using specified client + * local. I18N Support. + * + * @param clientLocale Locale to be used for localization + * @return string representation of the certificate + */ + public String toString(Locale clientLocale) { + return toString(clientLocale, 0, 0, 0); + } + + public String toString(Locale clientLocale, long crlSize, long pageStart, long pageSize) { + + //get I18N resources + ResourceBundle resource = ResourceBundle.getBundle( + PrettyPrintResources.class.getName()); + DateFormat dateFormater = DateFormat.getDateTimeInstance( + DateFormat.FULL, DateFormat.FULL, clientLocale); + //get timezone and timezone ID + String tz = " "; + String tzid = " "; + + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(4) + resource.getString( + PrettyPrintResources.TOKEN_CRL) + "\n"); + sb.append(pp.indent(8) + resource.getString( + PrettyPrintResources.TOKEN_DATA) + "\n"); + sb.append(pp.indent(12) + resource.getString( + PrettyPrintResources.TOKEN_VERSION) + " v"); + sb.append((mCRL.getVersion() + 1) + "\n"); + sb.append(pp.indent(12) + resource.getString( + PrettyPrintResources.TOKEN_SIGALG) + mCRL.getSigAlgName() + + " - " + mCRL.getSigAlgOID() + "\n"); + sb.append(pp.indent(12) + resource.getString( + PrettyPrintResources.TOKEN_ISSUER) + + mCRL.getIssuerDN().toString() + "\n"); + // Format thisUpdate + String thisUpdate = dateFormater.format(mCRL.getThisUpdate()); + + // get timezone and timezone ID + if (TimeZone.getDefault() != null) { + tz = TimeZone.getDefault().getDisplayName( + TimeZone.getDefault().inDaylightTime( + mCRL.getThisUpdate()), + TimeZone.SHORT, + clientLocale); + tzid = TimeZone.getDefault().getID(); + } + // Specify ThisUpdate + if (tz.equals(tzid) || tzid.equals(CUSTOM_LOCALE)) { + // Do NOT append timezone ID + sb.append(pp.indent(12) + + resource.getString( + PrettyPrintResources.TOKEN_THIS_UPDATE) + + thisUpdate + + "\n"); + } else { + // Append timezone ID + sb.append(pp.indent(12) + + resource.getString( + PrettyPrintResources.TOKEN_THIS_UPDATE) + + thisUpdate + + " " + tzid + "\n"); + } + // Check for presence of NextUpdate + if (mCRL.getNextUpdate() != null) { + // Format nextUpdate + String nextUpdate = dateFormater.format(mCRL.getNextUpdate()); + + // re-get timezone (just in case it is different . . .) + if (TimeZone.getDefault() != null) { + tz = TimeZone.getDefault().getDisplayName( + TimeZone.getDefault().inDaylightTime( + mCRL.getNextUpdate()), + TimeZone.SHORT, + clientLocale); + } + // Specify NextUpdate + if (tz.equals(tzid) || tzid.equals(CUSTOM_LOCALE)) { + // Do NOT append timezone ID + sb.append(pp.indent(12) + + resource.getString( + PrettyPrintResources.TOKEN_NEXT_UPDATE) + + nextUpdate + + "\n"); + } else { + // Append timezone ID + sb.append(pp.indent(12) + + resource.getString( + PrettyPrintResources.TOKEN_NEXT_UPDATE) + + nextUpdate + + " " + tzid + "\n"); + } + } + + if (crlSize > 0 && pageStart == 0 && pageSize == 0) { + sb.append(pp.indent(12) + resource.getString( + PrettyPrintResources.TOKEN_REVOKED_CERTIFICATES) + crlSize + "\n"); + } else if ((crlSize == 0 && pageStart == 0 && pageSize == 0) || + (crlSize > 0 && pageStart > 0 && pageSize > 0)) { + sb.append(pp.indent(12) + resource.getString( + PrettyPrintResources.TOKEN_REVOKED_CERTIFICATES)); + if (crlSize > 0 && pageStart > 0 && pageSize > 0) { + long upperLimit = (pageStart + pageSize - 1 > crlSize) ? crlSize : pageStart + pageSize - 1; + + sb.append("" + pageStart + "-" + upperLimit + " of " + crlSize); + } + sb.append("\n"); + + Set revokedCerts = mCRL.getRevokedCertificates(); + + if (revokedCerts != null) { + Iterator i = revokedCerts.iterator(); + long l = 1; + + while ((i.hasNext()) && ((crlSize == 0) || (pageStart + pageSize > l))) { + RevokedCertImpl revokedCert = + (RevokedCertImpl) i.next(); + + if ((crlSize == 0) || ((pageStart <= l) && (pageStart + pageSize > l))) { + sb.append(pp.indent(16) + resource.getString( + PrettyPrintResources.TOKEN_SERIAL) + "0x" + + revokedCert.getSerialNumber().toString(16).toUpperCase() + "\n"); + String revocationDate = + dateFormater.format(revokedCert.getRevocationDate()); + + // re-get timezone + // (just in case it is different . . .) + if (TimeZone.getDefault() != null) { + tz = TimeZone.getDefault().getDisplayName( + TimeZone.getDefault().inDaylightTime( + revokedCert.getRevocationDate()), + TimeZone.SHORT, + clientLocale); + } + // Specify revocationDate + if (tz.equals(tzid) || + tzid.equals(CUSTOM_LOCALE)) { + // Do NOT append timezone ID + sb.append(pp.indent(16) + + resource.getString( + PrettyPrintResources.TOKEN_REVOCATION_DATE) + + revocationDate + + "\n"); + } else { + // Append timezone ID + sb.append(pp.indent(16) + + resource.getString( + PrettyPrintResources.TOKEN_REVOCATION_DATE) + + revocationDate + + " " + tzid + "\n"); + } + if (revokedCert.hasExtensions()) { + sb.append(pp.indent(16) + resource.getString( + PrettyPrintResources.TOKEN_EXTENSIONS) + "\n"); + CRLExtensions crlExtensions = revokedCert.getExtensions(); + + if (crlExtensions != null) { + for (int k = 0; k < crlExtensions.size(); k++) { + Extension ext = (Extension) crlExtensions.elementAt(k); + ExtPrettyPrint extpp = new ExtPrettyPrint(ext, 20); + + sb.append(extpp.toString()); + } + } + } + } + l++; + } + } + } + + CRLExtensions crlExtensions = mCRL.getExtensions(); + + if (crlExtensions != null) { + sb.append(pp.indent(8) + resource.getString( + PrettyPrintResources.TOKEN_EXTENSIONS) + "\n"); + for (int k = 0; k < crlExtensions.size(); k++) { + Extension ext = (Extension) crlExtensions.elementAt(k); + ExtPrettyPrint extpp = new ExtPrettyPrint(ext, 12); + + sb.append(extpp.toString()); + } + } + + //take care of signature + sb.append(pp.indent(8) + resource.getString( + PrettyPrintResources.TOKEN_SIGNATURE) + "\n"); + //XXX I18N Algorithm Name ? + sb.append(pp.indent(12) + resource.getString( + PrettyPrintResources.TOKEN_ALGORITHM) + + mCRL.getSigAlgName() + " - " + mCRL.getSigAlgOID() + "\n"); + sb.append(pp.indent(12) + resource.getString( + PrettyPrintResources.TOKEN_SIGNATURE) + "\n"); + sb.append(pp.toHexString(mCRL.getSignature(), 16, 16)); + + } catch (Exception e) { + sb.append("\n\n" + pp.indent(4) + resource.getString( + PrettyPrintResources.TOKEN_DECODING_ERROR) + "\n\n"); + e.printStackTrace(); + } + + return sb.toString(); + } +} diff --git a/pki/base/util/src/netscape/security/util/DerEncoder.java b/pki/base/util/src/netscape/security/util/DerEncoder.java new file mode 100644 index 000000000..53bf27a6c --- /dev/null +++ b/pki/base/util/src/netscape/security/util/DerEncoder.java @@ -0,0 +1,40 @@ +// --- 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.util; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Interface to an object that knows how to write its own DER + * encoding to an output stream. + * + * @version 1.2 97/12/10 + * @author D. N. Hoover + */ +public interface DerEncoder { + + /** + * DER encode this object and write the results to a stream. + * + * @param out the stream on which the DER encoding is written. + */ + public void derEncode(OutputStream out) + throws IOException; + +} diff --git a/pki/base/util/src/netscape/security/util/DerInputBuffer.java b/pki/base/util/src/netscape/security/util/DerInputBuffer.java new file mode 100644 index 000000000..74ab9f705 --- /dev/null +++ b/pki/base/util/src/netscape/security/util/DerInputBuffer.java @@ -0,0 +1,185 @@ +// --- 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.util ; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.OutputStream; + + +/** + * DER input buffer ... this is the main abstraction in the DER library + * which actively works with the "untyped byte stream" abstraction. It + * does so with impunity, since it's not intended to be exposed to the + * anyone who could violate the "typed value stream" DER model and hence + * corrupt the input stream of DER values. + * + * @version 1.11 + * @author David Brownell + */ +class DerInputBuffer extends ByteArrayInputStream implements Cloneable { + + DerInputBuffer(byte[] buf) { super(buf); } + + DerInputBuffer(byte[] buf, int offset, int len) { + super(buf, offset, len); + } + + DerInputBuffer dup() { + try { + DerInputBuffer retval = (DerInputBuffer) clone (); + + retval.mark (Integer.MAX_VALUE); + return retval; + } catch (CloneNotSupportedException e) { + throw new IllegalArgumentException (e.toString ()); + } + } + + byte[] toByteArray() { + int len = available(); + if (len <= 0) + return null; + byte[] retval = new byte[len]; + + System.arraycopy(buf, pos, retval, 0, len); + return retval; + } + + int peek() throws IOException { + if (pos >= count) + throw new IOException ("out of data"); + else + return buf [pos]; + } + + /** + * Compares this DerInputBuffer for equality with the specified + * object. + */ + public boolean equals(Object other) { + if (other instanceof DerInputBuffer) + return equals ((DerInputBuffer) other); + else + return false; + } + + boolean equals(DerInputBuffer other) { + if (this == other) + return true; + + int max = this.available(); + if (other.available() != max) + return false; + for (int i = 0; i < max; i++) { + if (this.buf [this.pos + i] != other.buf [other.pos + i]) { + return false; + } + } + return true; + } + + void truncate(int len) throws IOException { + if (len > available ()) + throw new IOException ("insufficient data"); + count = pos + len; + } + + /** + * Returns the unsigned integer which takes up the specified number + * of bytes in this buffer. + */ + BigInt getUnsigned(int len) throws IOException { + if (len > available ()) + throw new IOException ("short read, getInteger"); + + /* + * A prepended zero is used to ensure that the integer is + * interpreted as unsigned even when the high order bit is + * zero. We don't support signed BigInts. + * + * Fix this here ... BigInts aren't expected to have these, + * and stuff like signing (sigsize = f(modulus)) misbehaves. + */ + if (len > 1 && buf [pos] == 0) { + len--; + skip (1); + } + + /* + * Consume the rest of the buffer, returning its value as + * an unsigned integer. + */ + byte[] bytes = new byte[len]; + + System.arraycopy (buf, pos, bytes, 0, len); + skip (len); + return new BigInt (bytes); + } + + /** + * Returns the bit string which takes up the rest of this buffer. + * This bit string must be byte-aligned. + */ + byte[] getBitString() { + if (pos >= count || buf [pos] != 0) + return null; + /* + * Just copy the data into an aligned, padded octet buffer, + * and consume the rest of the buffer. + */ + int len = available (); + byte[] retval = new byte[len - 1]; + + System.arraycopy (buf, pos + 1, retval, 0, len - 1); + pos = count; + return retval; + } + + /** + * Returns the bit string which takes up the rest of this buffer. + * The bit string need not be byte-aligned. + */ + BitArray getUnalignedBitString() { + if (pos >= count) + return null; + /* + * Just copy the data into an aligned, padded octet buffer, + * and consume the rest of the buffer. + */ + int len = available(); + byte[] bits = new byte[len - 1]; + int length = bits.length*8 - buf[pos]; // number of valid bits + + System.arraycopy(buf, pos + 1, bits, 0, len - 1); + + BitArray bitArray = new BitArray(length, bits); + pos = count; + return bitArray; + } + + /** + * Package-access method to optimize output operations + */ + void dump(OutputStream out, int length) throws IOException { + if (count < mark + length) + throw new IOException ("short DER value (encode)"); + out.write(buf,mark,length); + } + +} diff --git a/pki/base/util/src/netscape/security/util/DerInputStream.java b/pki/base/util/src/netscape/security/util/DerInputStream.java new file mode 100644 index 000000000..3e7705983 --- /dev/null +++ b/pki/base/util/src/netscape/security/util/DerInputStream.java @@ -0,0 +1,662 @@ +// --- 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.util; + +import java.io.InputStream; +import java.io.IOException; +import java.io.EOFException; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.Date; +import java.util.TimeZone; +import java.util.Vector; + +/** + * A DER input stream, used for parsing ASN.1 DER-encoded data such as + * that found in X.509 certificates. DER is a subset of BER/1, which has + * the advantage that it allows only a single encoding of primitive data. + * (High level data such as dates still support many encodings.) That is, + * it uses the "Definite" Encoding Rules (DER) not the "Basic" ones (BER). + * + *

Note that, like BER/1, DER streams are streams of explicitly + * tagged data values. Accordingly, this programming interface does + * not expose any variant of the java.io.InputStream interface, since + * that kind of input stream holds untagged data values and using that + * I/O model could prevent correct parsing of the DER data. + * + *

At this time, this class supports only a subset of the types of DER + * data encodings which are defined. That subset is sufficient for parsing + * most X.509 certificates. + * + * @version 1.35 + * + * @author David Brownell + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class DerInputStream { + /* + * This version only supports fully buffered DER. This is easy to + * work with, though if large objects are manipulated DER becomes + * awkward to deal with. That's where BER is useful, since BER + * handles streaming data relatively well. + */ + DerInputBuffer buffer; + + /** + * Create a DER input stream from a data buffer. The buffer is not + * copied, it is shared. Accordingly, the buffer should be treated + * as read-only. + * + * @param data the buffer from which to create the string (CONSUMED) + */ + public DerInputStream(byte[] data) { + buffer = new DerInputBuffer(data); + buffer.mark(Integer.MAX_VALUE); + } + + /** + * Create a DER input stream from part of a data buffer. + * The buffer is not copied, it is shared. Accordingly, the + * buffer should be treated as read-only. + * + * @param data the buffer from which to create the string (CONSUMED) + * @param offset the first index of data which will + * be read as DER input in the new stream + * @param len how long a chunk of the buffer to use, + * starting at "offset" + */ + public DerInputStream(byte[] data, int offset, int len) { + buffer = new DerInputBuffer(data, offset, len); + buffer.mark(Integer.MAX_VALUE); + } + + DerInputStream(DerInputBuffer buf) { + buffer = buf; + buffer.mark(Integer.MAX_VALUE); + } + + /** + * Creates a new DER input stream from part of this input stream. + * + * @param len how long a chunk of the current input stream to use, + * starting at the current position. + * @param do_skip true if the existing data in the input stream should + * be skipped. If this value is false, the next data read + * on this stream and the newly created stream will be the + * same. + */ + public DerInputStream subStream(int len, boolean do_skip) + throws IOException { + DerInputBuffer newbuf = buffer.dup(); + + newbuf.truncate(len); + if (do_skip) + buffer.skip(len); + return new DerInputStream(newbuf); + } + + /** + * Return what has been written to this DerInputStream + * as a byte array. Useful for debugging. + */ + public byte[] toByteArray() { + return buffer.toByteArray(); + } + + /* + * PRIMITIVES -- these are "universal" ASN.1 simple types. + * + * INTEGER, BIT STRING, OCTET STRING, NULL + * OBJECT IDENTIFIER, SEQUENCE (OF), SET (OF) + * PrintableString, T61String, IA5String, UTCTime + */ + + /** + * Get an (unsigned) integer from the input stream. + */ + public BigInt getInteger() throws IOException { + if (buffer.read() != DerValue.tag_Integer) + throw new IOException("DER input, Integer tag error"); + + return buffer.getUnsigned(getLength(buffer)); + } + + /** + * Get a bit string from the input stream. Only octet-aligned + * bitstrings (multiples of eight bits in length) are handled + * by this method. + */ + public byte[] getBitString() throws IOException { + if (buffer.read() != DerValue.tag_BitString) + throw new IOException("DER input not an bit string"); + int length = getLength(buffer); + + /* + * This byte affects alignment and padding (for the last byte). + * Use getUnalignedBitString() for none 8-bit aligned bit strings. + */ + if (buffer.read() != 0) + return null; + length--; + + /* + * Just read the data into an aligned, padded octet buffer. + */ + byte[] retval = new byte[length]; + if (buffer.read(retval) != length) + throw new IOException("short read of DER bit string"); + return retval; + } + + /** + * Get a bit string from the input stream. The bit string need + * not be byte-aligned. + */ + public BitArray getUnalignedBitString() throws IOException { + if (buffer.read() != DerValue.tag_BitString) + throw new IOException("DER input not a bit string"); + + int length = getLength(buffer) - 1; + + /* + * First byte = number of excess bits in the last octet of the + * representation. + */ + int validBits = length*8 - buffer.read(); + + byte[] repn = new byte[length]; + + if (buffer.read(repn) != length) + throw new IOException("short read of DER bit string"); + return new BitArray(validBits, repn); + } + + /** + * Returns an ASN.1 OCTET STRING from the input stream. + */ + public byte[] getOctetString() throws IOException { + if (buffer.read() != DerValue.tag_OctetString) + throw new IOException("DER input not an octet string"); + + int length = getLength(buffer); + byte[] retval = new byte[length]; + if (buffer.read(retval) != length) + throw new IOException("short read of DER octet string"); + + return retval; + } + + /** + * Returns the asked number of bytes from the input stream. + */ + public void getBytes(byte[] val) throws IOException { + if (val.length != 0) { + if (buffer.read(val) != val.length) { + throw new IOException("short read of DER octet string"); + } + } + } + + /** + * Reads an encoded null value from the input stream. + */ + public void getNull() throws IOException { + if (buffer.read() != DerValue.tag_Null || buffer.read() != 0) + throw new IOException("getNull, bad data"); + } + + /** + * Reads an X.200 style Object Identifier from the stream. + */ + public ObjectIdentifier getOID() throws IOException { + return new ObjectIdentifier(this); + } + + /** + * Return a sequence of encoded entities. ASN.1 sequences are + * ordered, and they are often used, like a "struct" in C or C++, + * to group data values. They may have optional or context + * specific values. + * + * @param startLen guess about how long the sequence will be + * (used to initialize an auto-growing data structure) + * @return array of the values in the sequence + */ + public DerValue[] getSequence(int startLen) throws IOException { + int b = buffer.read(); + if (b != DerValue.tag_Sequence) + throw new IOException("Sequence tag error " + b); + return readVector(startLen); + } + + /** + * Return a set of encoded entities. ASN.1 sets are unordered, + * though DER may specify an order for some kinds of sets (such + * as the attributes in an X.500 relative distinguished name) + * to facilitate binary comparisons of encoded values. + * + * @param startLen guess about how large the set will be + * (used to initialize an auto-growing data structure) + * @return array of the values in the sequence + */ + public DerValue[] getSet(int startLen) throws IOException { + if (buffer.read() != DerValue.tag_Set) + throw new IOException("Set tag error"); + return readVector(startLen); + } + + /** + * Return a set of encoded entities. ASN.1 sets are unordered, + * though DER may specify an order for some kinds of sets (such + * as the attributes in an X.500 relative distinguished name) + * to facilitate binary comparisons of encoded values. + * + * @param startLen guess about how large the set will be + * (used to initialize an auto-growing data structure) + * @param implicit if true tag is assumed implicit. + * @return array of the values in the sequence + */ + public DerValue[] getSet(int startLen, boolean implicit) throws IOException { + int tag = buffer.read(); + if (!implicit) { + if (tag != DerValue.tag_Set) { + throw new IOException("Set tag error"); + } + } + return (readVector(startLen)); + } + + /* + * Read a "vector" of values ... set or sequence have the + * same encoding, except for the initial tag, so both use + * this same helper routine. + */ + protected DerValue[] readVector(int startLen) throws IOException { + int len = getLength(buffer); + DerInputStream newstr; + + if (len == 0) + // return empty array instead of null, which should be + // used only for missing optionals + return new DerValue[0]; + + /* + * Create a temporary stream from which to read the data, + * unless it's not really needed. + */ + if (buffer.available() == len) + newstr = this; + else + newstr = subStream(len, true); + + /* + * Pull values out of the stream. + */ + Vector vec = new Vector(startLen); + DerValue value; + + do { + value = new DerValue(newstr.buffer); + vec.addElement(value); + } while (newstr.available() > 0); + + if (newstr.available() != 0) + throw new IOException("extra data at end of vector"); + + /* + * Now stick them into the array we're returning. + */ + int i, max = vec.size(); + DerValue[] retval = new DerValue[max]; + + for (i = 0; i < max; i++) + retval[i] = (DerValue) vec.elementAt(i); + + return retval; + } + + /** + * Get a single DER-encoded value from the input stream. + * It can often be useful to pull a value from the stream + * and defer parsing it. For example, you can pull a nested + * sequence out with one call, and only examine its elements + * later when you really need to. + */ + public DerValue getDerValue() throws IOException { + return new DerValue(buffer); + } + + public String getPrintableString() throws IOException + { + return (new DerValue(buffer)).getPrintableString(); + } + + public String getT61String() throws IOException + { + return (new DerValue(buffer)).getT61String(); + } + + public String getIA5String() throws IOException + { + return (new DerValue(buffer)).getIA5String(); + } + + public String getBMPString () throws IOException + { + return (new DerValue(buffer)).getBMPString(); + } + + public String getUniversalString () throws IOException + { + return (new DerValue(buffer)).getUniversalString(); + } + + /** + * Get a UTC encoded time value from the input stream. + */ + public Date getUTCTime() throws IOException { + if (buffer.read() != DerValue.tag_UtcTime) + throw new IOException("DER input, UTCtime tag invalid "); + if (buffer.available() < 11) + throw new IOException("DER input, UTCtime short input"); + + int len = getLength(buffer); + + if (len < 11 || len > 17) + throw new IOException("DER getUTCTime length error"); + + /* + * UTC time encoded as ASCII chars, YYMMDDhhmmss. + * If YY <= 50, we assume 20YY; + * if YY > 50, we assume 19YY, as per IETF-PKIX part I. + */ + int year, month, day, hour, minute, second; + + year = 10 * Character.digit((char)buffer.read(), 10); + year += Character.digit((char)buffer.read(), 10); + if (year <= 50) // origin 2000 + year += 2000; + else + year += 1900; // origin 1900 + + month = 10 * Character.digit((char)buffer.read(), 10); + month += Character.digit((char)buffer.read(), 10); + month -= 1; // months are 0-11 + + day = 10 * Character.digit((char)buffer.read(), 10); + day += Character.digit((char)buffer.read(), 10); + + hour = 10 * Character.digit((char)buffer.read(), 10); + hour += Character.digit((char)buffer.read(), 10); + + minute = 10 * Character.digit((char)buffer.read(), 10); + minute += Character.digit((char)buffer.read(), 10); + + len -= 10; + + /** + * We allow for non-encoded seconds, even though the + * IETF-PKIX specification says that the seconds should + * always be encoded even if it is zero. + */ + + if (len == 3 || len == 7) { + second = 10 * Character.digit((char)buffer.read(), 10); + second += Character.digit((char)buffer.read(), 10); + len -= 2; + } else + second = 0; + + if (month < 0 || day <= 0 + || month > 11 || day > 31 || hour >= 24 + || minute >= 60 || second >= 60) + throw new IOException("Parse UTC time, invalid format"); + + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + cal.set(year, month, day, hour, minute, second); + cal.set(Calendar.MILLISECOND, 0); /* To clear millisecond field */ + cal.set(Calendar.ERA, GregorianCalendar.AD); + Date readDate = cal.getTime(); + long utcTime = readDate.getTime(); + + /* + * Finally, "Z" or "+hhmm" or "-hhmm" ... offsets change hhmm + */ + if (! (len == 1 || len == 5)) + throw new IOException("Parse UTC time, invalid offset"); + + switch (buffer.read()) { + case '+': + { + int Htmp = 10 * Character.digit((char)buffer.read(), 10); + Htmp += Character.digit((char)buffer.read(), 10); + int Mtmp = 10 * Character.digit((char)buffer.read(), 10); + Mtmp += Character.digit((char)buffer.read(), 10); + + if (Htmp >=24 || Mtmp >= 60) + throw new IOException("Parse UTCtime, +hhmm"); + + utcTime += ((Htmp * 60) + Mtmp) * 60 * 1000; + } + break; + + case '-': + { + int Htmp = 10 * Character.digit((char)buffer.read(), 10); + Htmp += Character.digit((char)buffer.read(), 10); + int Mtmp = 10 * Character.digit((char)buffer.read(), 10); + Mtmp += Character.digit((char)buffer.read(), 10); + + if (Htmp >=24 || Mtmp >= 60) + throw new IOException("Parse UTCtime, -hhmm"); + + utcTime -= ((Htmp * 60) + Mtmp) * 60 * 1000; + } + break; + + case 'Z': + break; + + default: + throw new IOException("Parse UTCtime, garbage offset"); + } + readDate.setTime(utcTime); + return readDate; + } + + /** + * Get a Generalized encoded time value from the input stream. + */ + public Date getGeneralizedTime() throws IOException { + if (buffer.read () != DerValue.tag_GeneralizedTime) + throw new IOException ("DER input, GeneralizedTime tag invalid "); + + if (buffer.available() < 13) + throw new IOException ("DER input, GeneralizedTime short input"); + + int len = getLength (buffer); + + /* + * Generalized time encoded as ASCII chars, YYYYMMDDhhmm[ss] + */ + int year, month, day, hour, minute, second; + + year = 1000 * Character.digit ((char)buffer.read (), 10); + year += 100 * Character.digit ((char)buffer.read (), 10); + year += 10 * Character.digit ((char)buffer.read (), 10); + year += Character.digit ((char)buffer.read (), 10); + + month = 10 * Character.digit ((char)buffer.read (), 10); + month += Character.digit ((char)buffer.read (), 10); + month -= 1; // Calendar months are 0-11 + + day = 10 * Character.digit ((char)buffer.read (), 10); + day += Character.digit ((char)buffer.read (), 10); + + hour = 10 * Character.digit ((char)buffer.read (), 10); + hour += Character.digit ((char)buffer.read (), 10); + + minute = 10 * Character.digit ((char)buffer.read (), 10); + minute += Character.digit ((char)buffer.read (), 10); + + len -= 12; + + /** + * We allow for non-encoded seconds, even though the + * IETF-PKIX specification says that the seconds should + * always be encoded even if it is zero. + */ + + if (len == 3 || len == 7) { + second = 10 * Character.digit ((char)buffer.read (), 10); + second += Character.digit ((char)buffer.read (), 10); + len -= 2; + } else + second = 0; + + if (month < 0 || day <= 0 + || month > 11 || day > 31 || hour >= 24 + || minute >= 60 || second >= 60) + throw new IOException("Parse Generalized time, invalid format"); + +/* Shouldn't this construct a Gregorian calendar directly??? + * We don't really want locale dependant processing here */ + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + cal.set(year, month, day, hour, minute, second); + cal.set(Calendar.MILLISECOND, 0); /* To clear millisecond field */ + cal.set(Calendar.ERA, GregorianCalendar.AD); + Date readDate = cal.getTime(); + long utcTime = readDate.getTime(); + + /* + * Finally, "Z" or "+hhmm" or "-hhmm" ... offsets change hhmm + */ + if (! (len == 1 || len == 5)) + throw new IOException ("Parse Generalized time, invalid offset"); + + switch (buffer.read ()) { + case '+': + { + int Htmp = 10 * Character.digit((char)buffer.read(), 10); + Htmp += Character.digit((char)buffer.read(), 10); + int Mtmp = 10 * Character.digit((char)buffer.read(), 10); + Mtmp += Character.digit((char)buffer.read(), 10); + + if (Htmp >=24 || Mtmp >= 60) + throw new IOException("Parse GeneralizedTime, +hhmm"); + + utcTime += ((Htmp * 60) + Mtmp) * 60 * 1000; + } + break; + + case '-': + { + int Htmp = 10 * Character.digit((char)buffer.read(), 10); + Htmp += Character.digit((char)buffer.read(), 10); + int Mtmp = 10 * Character.digit((char)buffer.read(), 10); + Mtmp += Character.digit((char)buffer.read(), 10); + + if (Htmp >=24 || Mtmp >= 60) + throw new IOException("Parse GeneralizedTime, -hhmm"); + + utcTime -= ((Htmp * 60) + Mtmp) * 60 * 1000; + } + break; + + case 'Z': + break; + + default: + throw new IOException ("Parse GeneralizedTime, garbage offset"); + } + readDate.setTime(utcTime); + return readDate; + } + + /* + * Get a byte from the input stream. + */ + // package private + int getByte() throws IOException { + return (0x00ff & buffer.read()); + } + + public int peekByte() throws IOException { + return buffer.peek (); + } + + // package private + int getLength() throws IOException { + return getLength (buffer); + } + + /* + * Get a length from the input stream, allowing for at most 32 bits of + * encoding to be used. (Not the same as getting a tagged integer!) + */ + static int getLength(InputStream in) throws IOException { + int value, tmp; + + tmp = in.read (); + if ((tmp & 0x080) == 0x00) { // 1 byte datum? + value = tmp; + } else { // no, more ... + tmp &= 0x07f; + + /* + * NOTE: tmp == 0 indicates BER encoded data. + * tmp > 4 indicates more than 4Gb of data. + */ + if (tmp <= 0 || tmp > 4) + throw new IOException("DerInput.getLength(): lengthTag=" + + tmp + ", " + + ((tmp == 0) ? "Indefinite length encoding not supported" + + " or incorrect DER encoding." + : "too big.")); + + for (value = 0; tmp > 0; tmp --) { + value <<= 8; + value += 0x0ff & in.read (); + } + } + return value; + } + + /** + * Mark the current position in the buffer, so that + * a later call to reset will return here. + */ + public void mark (int value) { buffer.mark (value); } + + + /** + * Return to the position of the last mark + * call. A mark is implicitly set at the beginning of + * the stream when it is created. + */ + public void reset () { buffer.reset (); } + + + /** + * Returns the number of bytes available for reading. + * This is most useful for testing whether the stream is + * empty. + */ + public int available () { return buffer.available (); } +} diff --git a/pki/base/util/src/netscape/security/util/DerOutputStream.java b/pki/base/util/src/netscape/security/util/DerOutputStream.java new file mode 100644 index 000000000..618afc095 --- /dev/null +++ b/pki/base/util/src/netscape/security/util/DerOutputStream.java @@ -0,0 +1,749 @@ +// --- 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.util; + +import java.io.FilterOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.*; + +import java.util.Comparator; +import java.util.Arrays; + +import sun.io.CharToByteConverter; + +/** + * Output stream marshaling DER-encoded data. This is eventually provided + * in the form of a byte array; there is no advance limit on the size of + * that byte array. + * + *

At this time, this class supports only a subset of the types of + * DER data encodings which are defined. That subset is sufficient for + * generating most X.509 certificates. + * + * @version 1.32 + * + * @author David Brownell + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class DerOutputStream +extends ByteArrayOutputStream implements DerEncoder { + /** + * Construct an DER output stream. + * + * @param size how large a buffer to preallocate. + */ + public DerOutputStream(int size) { super(size); } + + /** + * Construct an DER output stream. + */ + public DerOutputStream() { } + + /** + * Writes tagged, pre-marshaled data. This calcuates and encodes + * the length, so that the output data is the standard triple of + * { tag, length, data } used by all DER values. + * + * @param tag the DER value tag for the data, such as + * DerValue.tag_Sequence + * @param buf buffered data, which must be DER-encoded + */ + public void write(byte tag, byte[] buf) throws IOException { + write(tag); + putLength(buf.length); + write(buf, 0, buf.length); + } + + /** + * Writes tagged data using buffer-to-buffer copy. As above, + * this writes a standard DER record. This is often used when + * efficiently encapsulating values in sequences. + * + * @param tag the DER value tag for the data, such as + * DerValue.tag_Sequence + * @param out buffered data + */ + public void write(byte tag, DerOutputStream out) throws IOException { + write(tag); + putLength(out.count); + write(out.buf, 0, out.count); + } + + /** + * Writes implicitly tagged data using buffer-to-buffer copy. As above, + * this writes a standard DER record. This is often used when + * efficiently encapsulating implicitly tagged values. + * + * @param tag the DER value of the context-specific tag that replaces + * original tag of the value in the output , such as in + *

+     *		  [N] IMPLICIT 
+     * 
+ * For example, FooLength [1] IMPLICIT INTEGER, with value=4; + * would be encoded as "81 01 04" whereas in explicit + * tagging it would be encoded as "A1 03 02 01 04". + * Notice that the tag is A1 and not 81, this is because with + * explicit tagging the form is always constructed. + * @param value original value being implicitly tagged + */ + public void writeImplicit(byte tag, DerOutputStream value) + throws IOException { + write(tag); + write(value.buf, 1, value.count-1); + } + + /** + * Marshals pre-encoded DER value onto the output stream. + */ + public void putDerValue(DerValue val) throws IOException { + val.encode(this); + } + + /* + * PRIMITIVES -- these are "universal" ASN.1 simple types. + * + * BOOLEAN, INTEGER, BIT STRING, OCTET STRING, NULL + * OBJECT IDENTIFIER, SEQUENCE(OF), SET(OF) + * PrintableString, T61String, IA5String, UTCTime + */ + + /** + * Marshals a DER boolean on the output stream. + */ + public void putBoolean(boolean val) throws IOException { + write(DerValue.tag_Boolean); + putLength(1); + if (val) { + write(0xff); + } else { + write(0); + } + } + + /** + * Marshals a DER unsigned integer on the output stream. + */ + public void putInteger(BigInt i) throws IOException + { + putUnsignedInteger(i.toByteArray()); + } + + /** + * Marshals a DER unsigned integer on the output stream. + */ + public void putUnsignedInteger(byte [] integerBytes) throws IOException { + + write(DerValue.tag_Integer); + if ((integerBytes [0] & 0x080) != 0) { + /* + * prepend zero so it's not read as a negative number + */ + putLength(integerBytes.length + 1); + write(0); + } else + putLength(integerBytes.length); + write(integerBytes, 0, integerBytes.length); + } + + /** + * Marshals a DER enumerated value on the output stream. + */ + public void putEnumerated(int i) throws IOException + { + write(DerValue.tag_Enumerated); + + int bytemask = 0xff000000; + int signmask = 0x80000000; + int length; + if ((i & 0x80000000) != 0) { + // negative case + for (length = 4; length > 1; --length) { + if((i & bytemask) != bytemask) + break; + bytemask = bytemask >>> 8; + signmask = signmask >>> 8; + } + if ((i & signmask) == 0) { + // ensure negative case + putLength(length+1); + write(0xff); + } else { + putLength(length); + } + // unrolled loop + switch (length) { + case 4: + write((byte)(i >>> 24)); + case 3: + write((byte)(i >>> 16)); + case 2: + write((byte)(i >>> 8)); + case 1: + write((byte)i); + } + } else { + // positive case + for (length = 4; length > 0; --length) { + if((i & bytemask) != 0) + break; + bytemask = bytemask >>> 8; + signmask = signmask >>> 8; + } + if ((i & signmask) != 0) { + // ensure posititive case + putLength(length+1); + write(0x00); + } else { + putLength(length); + } + // unrolled loop + switch (length) { + case 4: + write((byte)(i >>> 24)); + case 3: + write((byte)(i >>> 16)); + case 2: + write((byte)(i >>> 8)); + case 1: + write((byte)i); + } + } + } + + /** + * Marshals a DER bit string on the output stream. The bit + * string must be byte-aligned. + * + * @param bits the bit string, MSB first + */ + public void putBitString(byte[] bits) throws IOException { + write(DerValue.tag_BitString); + putLength(bits.length + 1); + write(0); // all of last octet is used + write(bits); + } + + /** + * Converts a boolean array to a BitArray. Trims trailing 0 bits + * in accordance with DER encoding standard. We assume the input is not + * null. + */ + private static BitArray toBitArray(boolean[] bitString) { + if( bitString.length == 0 ) { + return new BitArray(bitString); + } + + // find index of last 1 bit. -1 if there aren't any + int i; + for(i=bitString.length-1; i >= 0; i--) { + if(bitString[i]) { + break; + } + } + int length = i+1; + + // if length changed, copy to new appropriately-sized array + if(length != bitString.length) { + boolean[] newBitString = new boolean[length]; + System.arraycopy(bitString, 0, newBitString, 0, length); + bitString = newBitString; + } + + return new BitArray(bitString); + } + + /** + * Converts bit string to a BitArray, stripping off trailing 0 bits. + * We assume that the bit string is not null. + */ + private static BitArray toBitArray(byte[] bitString) { + // compute length in bits of bit string + int length, i; + int maxIndex = 0; + + if( bitString.length == 0 ) { + return new BitArray(0, bitString); + } + + // find the index of the last byte with a 1 bit + for( i = 0; i < bitString.length; i++) { + if( bitString[i] != 0 ) { + maxIndex = i; + } + } + byte lastByte = bitString[maxIndex]; + length = (maxIndex+1) * 8; // maximum, might reduce in next step + + // now find the last 1 bit in this last byte + for(i=1; i <= 0x80; i <<= 1) { + if( (lastByte & i) == 0 ) { + length--; + } else { + break; + } + } + return new BitArray(length, bitString); + } + + + /** + * Marshals a DER bit string on the output stream. + * The bit strings need not be byte-aligned. + * + * @param bits the bit string, MSB first + */ + public void putUnalignedBitString(BitArray ba) throws IOException { + byte[] bits = ba.toByteArray(); + + write(DerValue.tag_BitString); + putLength(bits.length + 1); + write(bits.length*8 - ba.length()); // excess bits in last octet + write(bits); + } + + /** + * Marshals a DER bit string on the output stream. + * All trailing 0 bits will be stripped off in accordance with DER + * encoding. + * + * @param bits the bit string, MSB first + */ + public void putUnalignedBitString(byte[] bitString) throws IOException { + putUnalignedBitString( toBitArray(bitString) ); + } + + /** + * Marshals a DER bit string on the output stream. + * All trailing 0 bits will be stripped off in accordance with DER + * encoding. + * + * @param bits the bit string as an array of booleans. + */ + public void putUnalignedBitString(boolean[] bitString) throws IOException { + putUnalignedBitString( toBitArray(bitString) ); + } + + /** + * DER-encodes an ASN.1 OCTET STRING value on the output stream. + * + * @param octets the octet string + */ + public void putOctetString(byte[] octets) throws IOException { + write(DerValue.tag_OctetString, octets); + } + + /** + * Marshals a DER "null" value on the output stream. These are + * often used to indicate optional values which have been omitted. + */ + public void putNull() throws IOException { + write(DerValue.tag_Null); + putLength(0); + } + + /** + * Marshals an object identifier (OID) on the output stream. + * Corresponds to the ASN.1 "OBJECT IDENTIFIER" construct. + */ + public void putOID(ObjectIdentifier oid) throws IOException { + oid.encode(this); + } + + /** + * Marshals a sequence on the output stream. This supports both + * the ASN.1 "SEQUENCE" (zero to N values) and "SEQUENCE OF" + * (one to N values) constructs. + */ + public void putSequence(DerValue[] seq) throws IOException { + DerOutputStream bytes = new DerOutputStream(); + int i; + + for (i = 0; i < seq.length; i++) + seq [i].encode(bytes); + + write(DerValue.tag_Sequence, bytes); + } + + /** + * Marshals the contents of a set on the output stream without + * ordering the elements. Ok for BER encoding, but not for DER + * encoding. + * + * For DER encoding, use orderedPutSet() or orderedPutSetOf(). + */ + public void putSet(DerValue[] set) throws IOException { + DerOutputStream bytes = new DerOutputStream(); + int i; + + for (i = 0; i < set.length; i++) + set [i].encode(bytes); + + write(DerValue.tag_Set, bytes); + } + + /** + * NSCP : + * Like putOrderSetOf, except not sorted. + * This may defy DER encoding but is needed for compatibility + * with communicator. + */ + public void putSet(byte tag, DerEncoder[] set) throws IOException { + putOrderedSet(tag, set, null); + } + + /** + * Marshals the contents of a set on the output stream. Sets + * are semantically unordered, but DER requires that encodings of + * set elements be sorted into ascending lexicographical order + * before being output. Hence sets with the same tags and + * elements have the same DER encoding. + * + * This method supports the ASN.1 "SET OF" construct, but not + * "SET", which uses a different order. + */ + public void putOrderedSetOf(byte tag, DerEncoder[] set) throws IOException { + putOrderedSet(tag, set, lexOrder); + } + + /** + * Marshals the contents of a set on the output stream. Sets + * are semantically unordered, but DER requires that encodings of + * set elements be sorted into ascending tag order + * before being output. Hence sets with the same tags and + * elements have the same DER encoding. + * + * This method supports the ASN.1 "SET" construct, but not + * "SET OF", which uses a different order. + */ + public void putOrderedSet(byte tag, DerEncoder[] set) throws IOException { + putOrderedSet(tag, set, tagOrder); + } + + /** + * Lexicographical order comparison on byte arrays, for ordering + * elements of a SET OF objects in DER encoding. + */ + private static ByteArrayLexOrder lexOrder = new ByteArrayLexOrder(); + + /** + * Tag order comparison on byte arrays, for ordering elements of + * SET objects in DER encoding. + */ + private static ByteArrayTagOrder tagOrder = new ByteArrayTagOrder(); + + /** + * Marshals a the contents of a set on the output stream with the + * encodings of its sorted in increasing order. + * + * @param order the order to use when sorting encodings of components. + */ + private void putOrderedSet(byte tag, DerEncoder[] set, + Comparator order) throws IOException { + DerOutputStream[] streams = new DerOutputStream[set.length]; + + for (int i = 0; i < set.length; i++) { + streams[i] = new DerOutputStream(); + set[i].derEncode(streams[i]); + } + + // order the element encodings + byte[][] bufs = new byte[streams.length][]; + for (int i = 0; i < streams.length; i++) { + bufs[i] = streams[i].toByteArray(); + } + if (order != null) { + Arrays.sort(bufs, order); + } + + DerOutputStream bytes = new DerOutputStream(); + for (int i = 0; i < streams.length; i++) { + bytes.write(bufs[i]); + } + write(tag, bytes); + + } + + /** + * Converts string to printable and writes to der output stream. + */ + public void putPrintableString(String s) throws IOException + { + putStringType(DerValue.tag_PrintableString, s); + } + + public void putVisibleString(String s) throws IOException + { + putStringType(DerValue.tag_VisibleString, s); + } + /** + * Marshals a string which is consists of BMP (unicode) characters + */ + public void putBMPString(String s) throws IOException + { + putStringType(DerValue.tag_BMPString, s); + } + + public void putGeneralString(String s) throws IOException + { + putStringType(DerValue.tag_GeneralString, s); + } + +// /* +// * T61 is an 8 bit extension to ASCII, escapes e.g. to Japanese +// */ +// void putT61String(String s) throws IOException +// { +// // XXX IMPLEMENT ME +// +// throw new IOException("DerOutputStream.putT61String() NYI"); +// } + +// /* +// * Universal String. +// */ +// void putUniversalString(String s) throws IOException +// { +// // XXX IMPLEMENT ME +// +// throw new IOException("DerOutputStream.putUniversalString() NYI"); +// } + + /** + * Marshals a string which is consists of IA5(ASCII) characters + */ + public void putIA5String(String s) throws IOException + { + putStringType(DerValue.tag_IA5String, s); + } + + public void putUTF8String(String s) throws IOException + { + putStringType(DerValue.tag_UTF8String, s); + } + + public void putStringType(byte tag, String s) throws IOException + { + int next_byte_index; + CharToByteConverter cbc; + byte buf[]; + try { + cbc = ASN1CharStrConvMap.getDefault().getCBC(tag); + if (cbc == null) + throw new IOException("No character to byte converter for tag"); + buf= new byte[cbc.getMaxBytesPerChar()*s.length()]; + // Don't use convertAll() here b/c it does not throw + // UnknownCharacterException. + next_byte_index = cbc.convert(s.toCharArray(), 0, s.length(), buf, 0, buf.length); + } + catch (java.io.CharConversionException e) { + throw new IOException("Not a valid string type "+ tag); + } + catch (IllegalAccessException e) { + throw new IOException("Cannot load CharToByteConverter class "+ + "for DER tag "+tag); + } + catch (InstantiationException e) { + throw new IOException("Cannot instantiate CharToByteConverter "+ + "class for DER tag "+tag); + } + + //next_byte_index = cbc.nextByteIndex(); + write(tag); + putLength(next_byte_index); + write(buf, 0, next_byte_index); + } + + private void put2DateBytes(byte[] buffer, int value, int offset) + { + int upper= value/10; + int lower = value%10; + buffer[offset] = (byte)((byte)upper + (byte)'0'); + buffer[offset+1] = (byte)((byte)lower + (byte)'0'); + } + + private static Calendar GMTGregorianCalendar = null; + + private Calendar getGMTGregorianCalendar() + { + if (GMTGregorianCalendar == null) { + TimeZone tz = TimeZone.getTimeZone("GMT"); + GMTGregorianCalendar = new GregorianCalendar(tz); + } + return (Calendar)GMTGregorianCalendar.clone(); + } + + public byte[] getDateBytes(Date d, boolean UTC) + { + + byte[] datebytes; + + if (UTC) { + datebytes = new byte[13]; + } + else { // generalized time has 4 digits for yr + datebytes = new byte[15]; + } + + Calendar cal = getGMTGregorianCalendar(); + cal.setTime(d); + + int i=0; + if (!UTC) { + put2DateBytes(datebytes,cal.get(Calendar.YEAR)/100,i); + i+= 2; + } + put2DateBytes(datebytes,cal.get(Calendar.YEAR)%100 ,i); + // Calendar's MONTH is zero-based + i+= 2; + put2DateBytes(datebytes,cal.get(Calendar.MONTH)+1 ,i); + i+= 2; + put2DateBytes(datebytes,cal.get(Calendar.DAY_OF_MONTH),i); + i+= 2; + put2DateBytes(datebytes,cal.get(Calendar.HOUR_OF_DAY) ,i); + i+= 2; + put2DateBytes(datebytes,cal.get(Calendar.MINUTE) ,i); + i+= 2; + put2DateBytes(datebytes,cal.get(Calendar.SECOND) ,i); + i+= 2; + // datebytes[i] = 'Z'; + datebytes[i] = (byte)'Z'; + + return datebytes; + } + + /** + * Marshals a DER UTC time/date value. + * + *

YYMMDDhhmmss{Z|+hhmm|-hhmm} ... emits only using Zulu time + * and with seconds (even if seconds=0) as per IETF-PKIX partI. + */ + public void putUTCTime(Date d) throws IOException { + /* + * Format the date. + */ + + + // This was the old code. Way too slow to be usable (stevep) + + // String pattern = "yyMMddHHmmss'Z'"; + // SimpleDateFormat sdf = new SimpleDateFormat(pattern); + // TimeZone tz = TimeZone.getTimeZone("GMT"); + // sdf.setTimeZone(tz); + // byte[] utc = (sdf.format(d)).getBytes(); + + byte[] datebytes = getDateBytes(d,true); // UTC = true + + /* + * Write the formatted date. + */ + write (DerValue.tag_UtcTime); + putLength(datebytes.length); + write(datebytes); + } + + /** + * Marshals a DER Generalized Time/date value. + * + *

YYYYMMDDhhmmss{Z|+hhmm|-hhmm} ... emits only using Zulu time + * and with seconds (even if seconds=0) as per IETF-PKIX partI. + */ + public void putGeneralizedTime(Date d) throws IOException { + /* + * Format the date. + */ + TimeZone tz = TimeZone.getTimeZone("GMT"); + + // This is way too slow to be usable (stevep) + String pattern = "yyyyMMddHHmmss'Z'"; + SimpleDateFormat sdf = new SimpleDateFormat(pattern); + sdf.setTimeZone(tz); + byte[] gt = (sdf.format(d)).getBytes(); + + /* + * Write the formatted date. + */ + write(DerValue.tag_GeneralizedTime); + putLength(gt.length); + write(gt); + } + + /** + * Put the encoding of the length in the stream. + * + * @param len the length of the attribute. + * @exception IOException on writing errors. + */ + public void putLength(int len) throws IOException { + if (len < 128) { + write ((byte)len); + + } else if (len < (1 << 8)) { + write ((byte)0x081); + write ((byte)len); + + } else if (len < (1 << 16)) { + write ((byte)0x082); + write ((byte) (len >> 8)); + write ((byte) len); + + } else if (len < (1 << 24)) { + write ((byte)0x083); + write ((byte) (len >> 16)); + write ((byte) (len >> 8)); + write ((byte) len); + + } else { + write ((byte)0x084); + write ((byte) (len >> 24)); + write ((byte) (len >> 16)); + write ((byte) (len >> 8)); + write ((byte) len); + } + } + + /** + * Put the tag of the attribute in the stream. + * + * @param class the tag class type, one of UNIVERSAL, CONTEXT, + * APPLICATION or PRIVATE + * @param form if true, the value is constructed, otherwise it is + * primitive. + * @param val the tag value + */ + public void putTag(byte tagClass, boolean form, byte val) { + byte tag = (byte) (tagClass | val); + if (form) { + tag |= (byte)0x20; + } + write (tag); + } + + /** + * Write the current contents of this DerOutputStream + * to an OutputStream. + * + * @exception IOException on output error. + */ + public void derEncode(OutputStream out) throws IOException { + out.write(toByteArray()); + } +} diff --git a/pki/base/util/src/netscape/security/util/DerValue.java b/pki/base/util/src/netscape/security/util/DerValue.java new file mode 100644 index 000000000..bc2bf45e3 --- /dev/null +++ b/pki/base/util/src/netscape/security/util/DerValue.java @@ -0,0 +1,715 @@ +// --- 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.util; + +import java.io.*; +import java.util.*; + +import sun.io.ByteToCharConverter; + +import netscape.security.x509.AVAValueConverter; +import netscape.security.x509.GenericValueConverter; + +/** + * Represents a single DER-encoded value. DER encoding rules are a subset + * of the "Basic" Encoding Rules (BER), but they only support a single way + * ("Definite" encoding) to encode any given value. + * + *

All DER-encoded data are triples {type, length, data}. This + * class represents such tagged values as they have been read (or constructed), + * and provides structured access to the encoded data. + * + *

At this time, this class supports only a subset of the types of DER + * data encodings which are defined. That subset is sufficient for parsing + * most X.509 certificates, and working with selected additional formats + * (such as PKCS #10 certificate requests, and some kinds of PKCS #7 data). + * + * @version 1.43 + * + * @author David Brownell + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class DerValue { + /** The tag class types */ + public static final byte TAG_UNIVERSAL = (byte)0x000; + public static final byte TAG_APPLICATION = (byte)0x040; + public static final byte TAG_CONTEXT = (byte)0x080; + public static final byte TAG_PRIVATE = (byte)0x0c0; + + /** The DER tag of the value; one of the tag_ constants. */ + public byte tag; + + protected DerInputBuffer buffer; + + /** + * The DER-encoded data of the value. + */ + public DerInputStream data; + + private int length; + + /* + * The type starts at the first byte of the encoding, and + * is one of these tag_* values. That may be all the type + * data that is needed. + */ + + /* + * These tags are the "universal" tags ... they mean the same + * in all contexts. (Mask with 0x1f -- five bits.) + */ + + /** Tag value indicating an ASN.1 "BOOLEAN" value. */ + public final static byte tag_Boolean = 0x01; + + /** Tag value indicating an ASN.1 "INTEGER" value. */ + public final static byte tag_Integer = 0x02; + + /** Tag value indicating an ASN.1 "BIT STRING" value. */ + public final static byte tag_BitString = 0x03; + + /** Tag value indicating an ASN.1 "OCTET STRING" value. */ + public final static byte tag_OctetString = 0x04; + + /** Tag value indicating an ASN.1 "NULL" value. */ + public final static byte tag_Null = 0x05; + + /** Tag value indicating an ASN.1 "OBJECT IDENTIFIER" value. */ + public final static byte tag_ObjectId = 0x06; + + /** Tag value including an ASN.1 "ENUMERATED" value */ + public final static byte tag_Enumerated = 0x0A; + + /** Tag value including a "printable" string */ + public final static byte tag_PrintableString = 0x13; + + public final static byte tag_VisibleString = 0x1A; + + /** Tag value including a "teletype" string */ + public final static byte tag_T61String = 0x14; + + /** Tag value including an ASCII string */ + public final static byte tag_IA5String = 0x16; + + /** Tag value indicating an ASN.1 "UTCTime" value. */ + public final static byte tag_UtcTime = 0x17; + + /** Tag value indicating an ASN.1 "GeneralizedTime" value. */ + public final static byte tag_GeneralizedTime = 0x18; + + /** Tag value indicating an ASN.1 "GeneralString" value. */ + public final static byte tag_GeneralString = 0x1B; + + /** Tag value indicating an ASN.1 "BMPString" value. */ + public final static byte tag_BMPString = 0x1E; + + /** Tag value indicating an ASN.1 "UniversalString" value. */ + public final static byte tag_UniversalString = 0x1C; + + /** Tag value indicating an ASN.1 "UTF8String" value. (since 1998) */ + public final static byte tag_UTF8String = 0x0C; + + // CONSTRUCTED seq/set + + /** Tag value indicating an ASN.1 + * "SEQUENCE" (zero to N elements, order is significant). */ + public final static byte tag_Sequence = 0x30; + + /** Tag value indicating an ASN.1 + * "SEQUENCE OF" (one to N elements, order is significant). */ + public final static byte tag_SequenceOf = 0x30; + + /** Tag value indicating an ASN.1 + * "SET" (zero to N members, order does not matter). */ + public final static byte tag_Set = 0x31; + + /** Tag value indicating an ASN.1 + * "SET OF" (one to N members, order does not matter). */ + public final static byte tag_SetOf = 0x31; + + /* + * These values are the high order bits for the other kinds of tags. + */ + boolean isUniversal() { return ((tag & 0x0c0) == 0x000); } + boolean isApplication() { return ((tag & 0x0c0) == 0x040); } + + /** + * Returns true iff the CONTEXT SPECIFIC bit is set in the type tag. + * This is associated with the ASN.1 "DEFINED BY" syntax. + */ + public boolean isContextSpecific() { return ((tag & 0x0c0) == 0x080); } + + /** + * Returns true iff the CONTEXT SPECIFIC TAG matches the passed tag. + */ + public boolean isContextSpecific(byte cntxtTag) { + if (!isContextSpecific ()) { + return false; + } + return ((tag & 0x01f) == cntxtTag); + } + + boolean isPrivate() { return ((tag & 0x0c0) == 0x0c0); } + + /** Returns true iff the CONSTRUCTED bit is set in the type tag. */ + public boolean isConstructed() { return ((tag & 0x020) == 0x020); } + + /** + * Creates a DER value from a string + * using a generic way of determining the proper tag for the string. + * Assumes the string is a Generic attribute value and uses + * the converter for generic string values to convert to the Der Value. + */ + public DerValue (String value) + throws IOException + { + AVAValueConverter genericValue = new GenericValueConverter(); + DerValue val; + + val = genericValue.getValue(value); + tag = val.tag; + buffer = val.buffer; + length = val.length; + data = val.data; + data.mark (Integer.MAX_VALUE); + } + + /** + * Creates a DerValue from a tag and some DER-encoded data. + * + * @param tag the DER type tag + * @param data the DER-encoded data + */ + public DerValue(byte tag, byte[] data) { + this.tag = tag; + buffer = new DerInputBuffer((byte[])data.clone()); + length = data.length; + this.data = new DerInputStream (buffer); + this.data.mark (Integer.MAX_VALUE); + } + + /* + * package private + */ + DerValue(DerInputBuffer in) throws IOException { + // NOTE: This must handle the special value used + // to terminate BER indefinite encodings (tag and + // length are both zero) + + // XXX must also parse BER-encoded constructed + // values such as sequences, sets... + + tag = (byte) in.read (); + length = DerInputStream.getLength (in); + + buffer = in.dup (); + buffer.truncate (length); + data = new DerInputStream (buffer); + + in.skip (length); + } + + /** + * Get an ASN.1/DER encoded datum from a buffer. The + * entire buffer must hold exactly one datum, including + * its tag and length. + * + * @param buf buffer holding a single DER-encoded datum. + */ + public DerValue(byte[] buf) throws IOException { + init (true, new ByteArrayInputStream (buf)); + } + + /** + * Get an ASN.1/DER encoded datum from part of a buffer. + * That part of the buffer must hold exactly one datum, including + * its tag and length. + * + * @param buf the buffer + * @param offset start point of the single DER-encoded dataum + * @param length how many bytes are in the encoded datum + */ + public DerValue(byte[] buf, int offset, int len) throws IOException { + init (true, new ByteArrayInputStream (buf, offset, len)); + } + + /** + * Get an ASN1/DER encoded datum from an input stream. The + * stream may have additional data following the encoded datum. + * + * @param in the input stream holding a single DER datum, + * which may be followed by additional data + */ + public DerValue(InputStream in) throws IOException { + init (false, in); + } + + /* + * helper routine + */ + private void init (boolean fullyBuffered, InputStream in) + throws IOException { + byte[] bytes; + + tag = (byte) in.read (); + length = DerInputStream.getLength (in); + + /* + if (length == 0) + return; + */ + + if (fullyBuffered && in.available () != length) + throw new IOException ("extra DER value data (constructor)"); + + bytes = new byte [length]; + + // n.b. readFully not needed in normal fullyBuffered case + DataInputStream dis = new DataInputStream (in); + + dis.readFully (bytes); + buffer = new DerInputBuffer (bytes); + data = new DerInputStream (buffer); + } + + /** + * Encode an ASN1/DER encoded datum onto a DER output stream. + */ + public void encode(DerOutputStream out) + throws IOException { + out.write (tag); + out.putLength (length); + buffer.dump(out,length); + + } + + /** + * Returns an ASN.1 BOOLEAN + * + * @return the boolean held in this DER value + */ + public boolean getBoolean() throws IOException { + if (tag != tag_Boolean) { + throw new IOException ("DerValue.getBoolean, not a BOOLEAN " + tag); + } + if (length != 1) { + throw new IOException ("DerValue.getBoolean, invalid length " + length); + } + if (buffer.read() != 0) { + return true; + } + return false; + } + + /** + * Returns an ASN.1 OBJECT IDENTIFIER. + * + * @return the OID held in this DER value + */ + public ObjectIdentifier getOID() throws IOException { + if (tag != tag_ObjectId) + throw new IOException ("DerValue.getOID, not an OID " + tag); + return new ObjectIdentifier (buffer); + } + + /** + * Returns an ASN.1 OCTET STRING + * + * @return the octet string held in this DER value + */ + public byte[] getOctetString() throws IOException { + if (tag != tag_OctetString) + throw new IOException ( + "DerValue.getOctetString, not an Octet String: " + tag); + + byte [] bytes = new byte [length]; + + if (buffer.read(bytes) != length) + throw new IOException("short read on DerValue buffer"); + return bytes; + } + + /** + * Returns an ASN.1 unsigned integer value of enumerated value. + * + * @return the (unsigned) integer held in this DER value + */ + public int getEnumerated () + throws IOException + { + if (tag != tag_Enumerated) + throw new IOException ("DerValue.getEnumerated, not an ENUMERATED " + tag); + if (length == 0) + return 0; + if (length > 4 || length < 1) + throw new IOException("DerValue.getEnumerated, invalid length " + length + "(must be between 1 and 4)"); + + int value = 0; + int nextbyte = buffer.read(); + if (nextbyte == -1) + throw new IOException("short read on DerValue buffer"); + // perform sign extension + value = (byte) nextbyte; + + for (int i = length - 1; i > 0; --i) { + nextbyte = buffer.read(); + if (nextbyte == -1) + throw new IOException("short read on DerValue buffer"); + value = 256 * value + nextbyte; + } + return value; + } + + /** + * Returns an ASN.1 unsigned INTEGER value. + * + * @return the (unsigned) integer held in this DER value + */ + public BigInt getInteger() throws IOException { + if (tag != tag_Integer) + throw new IOException ("DerValue.getInteger, not an int " + tag); + return buffer.getUnsigned (data.available ()); + } + + /** + * Returns an ASN.1 unsigned INTEGER value, the parameter determining + * if the tag is implicit. + * + * @param tagImplicit if true, ignores the tag value as it is + * assumed implicit. + * @return the (unsigned) integer held in this DER value + */ + public BigInt getInteger(boolean tagImplicit) throws IOException { + if (!tagImplicit) { + if (tag != tag_Integer) { + throw new IOException("DerValue.getInteger, not an int " + + tag); + } + } + return buffer.getUnsigned (data.available ()); + } + + /** + * Returns an ASN.1 BIT STRING value. The bit string must be byte-aligned. + * + * @return the bit string held in this value + */ + public byte[] getBitString() throws IOException { + if (tag != tag_BitString) + throw new IOException ( + "DerValue.getBitString, not a bit string " + tag); + + return buffer.getBitString (); + } + + /** + * Returns an ASN.1 BIT STRING value that need not be byte-aligned. + * + * @return a BitArray representing the bit string held in this value + */ + public BitArray getUnalignedBitString() throws IOException { + if (tag != tag_BitString) + throw new IOException( + "DerValue.getBitString, not a bit string " + tag); + + return buffer.getUnalignedBitString(); + } + + /** + * Returns the name component as a Java string, regardless of its + * encoding restrictions (ASCII, T61, Printable, etc). + */ + public String getAsString () throws IOException + { + AVAValueConverter genericValue = new GenericValueConverter(); + return genericValue.getAsString(this); + } + + /** + * Returns an ASN.1 BIT STRING value, with the tag assumed implicit + * based on the parameter. The bit string must be byte-aligned. + * + * @param tagImplicit if true, the tag is assumed implicit. + * @return the bit string held in this value + */ + public byte[] getBitString(boolean tagImplicit) throws IOException { + if (!tagImplicit) { + if (tag != tag_BitString) + throw new IOException ("DerValue.getBitString, not a bit string " + + tag); + } + return buffer.getBitString (); + } + + /** + * Returns an ASN.1 BIT STRING value, with the tag assumed implicit + * based on the parameter. The bit string need not be byte-aligned. + * + * @param tagImplicit if true, the tag is assumed implicit. + * @return the bit string held in this value + */ + public BitArray getUnalignedBitString(boolean tagImplicit) + throws IOException { + if (!tagImplicit) { + if (tag != tag_BitString) + throw new IOException("DerValue.getBitString, not a bit string " + + tag); + } + return buffer.getUnalignedBitString(); + } + + /** + * Returns an ASN.1 STRING value + * + * @return the printable string held in this value + */ + public String getPrintableString () + throws IOException { + if (tag != tag_PrintableString) + throw new IOException ( + "DerValue.getPrintableString, not a string " + tag); + + return getASN1CharString(); + } + + /* + * Internal utility ... returns a string regardless of what + * restrictions have been placed on its encoding. + */ + private String simpleGetString() throws IOException { + StringBuffer s = new StringBuffer(length); + try { + int temp = length; + + data.reset (); + while (temp-- > 0) + s.append ((char) data.getByte ()); + } catch (IOException e) { + return null; + } + return new String (s); + } + + /* + * @eturns a string if the DerValue is a ASN.1 character string type and + * if there is a ByteToChar converter for the type. Returns null otherwise. + */ + public String getASN1CharString() + throws IOException + { + ByteToCharConverter bcc; + int ret; + byte buf[]; + char cbuf[]; + + try { + bcc = ASN1CharStrConvMap.getDefault().getBCC(tag); + if (bcc == null) + return null; + + buf = new byte[length]; + cbuf = new char[bcc.getMaxCharsPerByte()*length]; + data.reset(); + data.getBytes(buf); + ret = bcc.convert(buf, 0, buf.length, cbuf, 0, cbuf.length); + } + catch (java.io.CharConversionException e) { + throw new IOException("Misformed DER value"); + } + catch (IllegalAccessException e) { + throw new IOException("Illegal Access loading ByteToCharConverter"); + } + catch (InstantiationException e) { + throw new IOException("Cannot instantiate ByteToCharConverter"); + } + return new String(cbuf, 0, ret); + } + + /** + * Returns an ASN.1 T61 (Teletype) STRING value + * + * @return the teletype string held in this value + */ + public String getT61String() throws IOException { + if (tag != tag_T61String) + throw new IOException ( + "DerValue.getT61String, not T61 " + tag); + + return getASN1CharString (); + } + + /** + * Returns an ASN.1 IA5 (ASCII) STRING value + * + * @return the ASCII string held in this value + */ + public String getIA5String() throws IOException { + if (tag != tag_IA5String) + throw new IOException ( + "DerValue.getIA5String, not IA5 " + tag); + + return getASN1CharString (); + } + + public String getBMPString () + throws IOException + { + if (tag != tag_BMPString) + throw new IOException ( + "DerValue.getBMPString, not BMP " + tag); + + return getASN1CharString (); + } + + public String getUniversalString () + throws IOException + { + if (tag != tag_UniversalString) + throw new IOException ( + "DerValue.getUniversalString, not UniversalString " + tag); + + return getASN1CharString (); + } + + public String getUTF8String () + throws IOException + { + if (tag != tag_UTF8String) + throw new IOException ( + "DerValue.getUTF8String, not UTF8String " + tag); + + return getASN1CharString (); + } + + /** + * Returns true iff the other object is a DER value which + * is bitwise equal to this one. + * + * @param other the object being compared with this one + */ + public boolean equals(Object other) { + if (other instanceof DerValue) + return equals ((DerValue)other); + else + return false; + } + + /** + * Bitwise equality comparison. DER encoded values have a single + * encoding, so that bitwise equality of the encoded values is an + * efficient way to establish equivalence of the unencoded values. + * + * @param other the object being compared with this one + */ + public boolean equals(DerValue other) { + data.reset (); + other.data.reset(); + if (this == other) + return true; + else if (tag != other.tag) { + return false; + } else { + return buffer.equals (other.buffer); + } + } + + /** + * Returns a printable representation of the value. + * + * @return printable representation of the value + */ + public String toString() { + try { + String s = getAsString(); + if (s != null) + return s; + if (tag == tag_Null) + return "[DerValue, null]"; + if (tag == tag_ObjectId) + return "OID." + getOID (); + + // integers + else + return "[DerValue, tag = " + tag + + ", length = " + length + "]"; + } catch (IOException e) { + throw new IllegalArgumentException ("misformatted DER value"); + } + } + + /** + * Returns a DER-encoded value, such that if it's passed to the + * DerValue constructor, a value equivalent to "this" is returned. + * + * @return DER-encoded value, including tag and length. + */ + public byte[] toByteArray() throws IOException { + DerOutputStream out = new DerOutputStream (); + + encode (out); + data.reset (); + return out.toByteArray (); + } + + /** + * For "set" and "sequence" types, this function may be used + * to return a DER stream of the members of the set or sequence. + * This operation is not supported for primitive types such as + * integers or bit strings. + */ + public DerInputStream toDerInputStream() throws IOException { + if (tag == tag_Sequence || tag == tag_Set) + return new DerInputStream (buffer); + throw new IOException ("toDerInputStream rejects tag type " + tag); + } + + /** + * Get the length of the encoded value. + */ + public int length() { + return length; + } + + /** + * Create the tag of the attribute. + * + * @param class the tag class type, one of UNIVERSAL, CONTEXT, + * APPLICATION or PRIVATE + * @param form if true, the value is constructed, otherwise it + * is primitive. + * @param val the tag value + */ + public static byte createTag(byte tagClass, boolean form, byte val) { + byte tag = (byte) (tagClass | val); + if (form) { + tag |= (byte)0x20; + } + return (tag); + } + + /** + * Set the tag of the attribute. Commonly used to reset the + * tag value used for IMPLICIT encodings. + * + * @param tag the tag value + */ + public void resetTag(byte tag) { + this.tag = tag; + } +} diff --git a/pki/base/util/src/netscape/security/util/ExtPrettyPrint.java b/pki/base/util/src/netscape/security/util/ExtPrettyPrint.java new file mode 100644 index 000000000..8db38c7a1 --- /dev/null +++ b/pki/base/util/src/netscape/security/util/ExtPrettyPrint.java @@ -0,0 +1,1556 @@ +// --- 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.util; + + +import java.io.*; +import java.util.*; +import java.text.*; +import java.math.BigInteger; +import netscape.security.util.*; +import netscape.security.extensions.*; +import netscape.security.x509.*; +import java.security.*; +import netscape.security.x509.CRLDistributionPointsExtension.Reason; + + +/** + * This class will display the certificate content in predefined + * format. + * + * @author Andrew Wnuk + * @version $Revision: 14564 $, $Date: 2007-05-01 10:40:13 -0700 (Tue, 01 May 2007) $ + */ +public class ExtPrettyPrint { + + /*========================================================== + * variables + *==========================================================*/ + private Extension mExt = null; + private ResourceBundle mResource = null; + private ResourceBundle resource = null; + private PrettyPrintFormat pp = null; + private int mIndentSize = 0; + + DateFormat dateFormater = null; + + /*========================================================== + * constructors + *==========================================================*/ + + public ExtPrettyPrint(Extension ext, int indentSize) { + mExt = ext; + mResource = ResourceBundle.getBundle(PrettyPrintResources.class.getName()); + mIndentSize = indentSize; + pp = new PrettyPrintFormat(":"); + } + + /*========================================================== + * public methods + *==========================================================*/ + + /** + * This method return string representation of the certificate + * in predefined format using specified client local. I18N Support. + * + * @param clientLocale Locale to be used for localization + * @return string representation of the certificate + */ + // public String toString(int indentSize) { + public String toString() { + + StringBuffer sb = new StringBuffer(); + + //check if the extension is known + if (mExt instanceof KeyUsageExtension) { + return getKeyUsage(); + } + if (mExt instanceof NSCertTypeExtension) { + return getCertType(); + } + if (mExt instanceof AuthorityKeyIdentifierExtension) { + return getAuthorityKeyIdentifier(); + } + if (mExt instanceof SubjectKeyIdentifierExtension) { + return getSubjectKeyIdentifier(); + } + if (mExt instanceof CRLReasonExtension) { + return getCRLReasonExtension(); + } + if (mExt instanceof BasicConstraintsExtension) { + return getBasicConstraintsExtension(); + } + if (mExt instanceof NSCCommentExtension) { + return getNSCCommentExtension(); + } + if (mExt instanceof NameConstraintsExtension) { + return getNameConstraintsExtension(); + } + if (mExt instanceof CRLNumberExtension) { + return getCRLNumberExtension(); + } + if (mExt instanceof DeltaCRLIndicatorExtension) { + return getDeltaCRLIndicatorExtension(); + } + if (mExt instanceof IssuerAlternativeNameExtension) { + return getIssuerAlternativeNameExtension(); + } + if (mExt instanceof SubjectAlternativeNameExtension) { + return getSubjectAlternativeNameExtension(); + } + if (mExt instanceof FreshestCRLExtension) { + return getFreshestCRLExtension(); + } + if (mExt instanceof CRLDistributionPointsExtension) { + return getCRLDistributionPointsExtension(); + } + if (mExt instanceof IssuingDistributionPointExtension) { + return getIssuingDistributionPointExtension(); + } + if (mExt instanceof ExtendedKeyUsageExtension) { + return getExtendedKeyUsageExtension(); + } + if (mExt instanceof AuthInfoAccessExtension) { + return getAuthInfoAccessExtension(); + } + if (mExt instanceof SubjectInfoAccessExtension) { + return getSubjectInfoAccessExtension(); + } + if (mExt instanceof OCSPNoCheckExtension) { + return getOCSPNoCheckExtension(); + } + if (mExt instanceof PrivateKeyUsageExtension) { + return getPrivateKeyUsageExtension(); + } + if (mExt instanceof InvalidityDateExtension) { + return getInvalidityDateExtension(); + } + if (mExt instanceof CertificateIssuerExtension) { + return getCertificateIssuerExtension(); + } + if (mExt instanceof HoldInstructionExtension) { + return getHoldInstructionExtension(); + } + if (mExt instanceof PolicyConstraintsExtension) { + return getPolicyConstraintsExtension(); + } + if (mExt instanceof PolicyMappingsExtension) { + return getPolicyMappingsExtension(); + } + if (mExt instanceof SubjectDirAttributesExtension) { + return getSubjectDirAttributesExtension(); + } + if (mExt instanceof CertificateScopeOfUseExtension) { + return getCertificateScopeOfUseExtension(); + } + if (mExt instanceof PresenceServerExtension) { + return getPresenceServerExtension(); + } + + if (mExt instanceof InhibitAnyPolicyExtension) { + return getInhibitAnyPolicyExtension(); + } + + if (mExt instanceof CertificatePoliciesExtension) { + return getCertificatePoliciesExtension(); + } + + //unknown cert extension + try { + String extName = OIDMap.getName(mExt.getExtensionId()); + + if (extName == null) + sb.append(pp.indent(mIndentSize) + mResource.getString( + PrettyPrintResources.TOKEN_IDENTIFIER) + + mExt.getExtensionId().toString() + "\n"); + else + sb.append(pp.indent(mIndentSize) + mResource.getString( + PrettyPrintResources.TOKEN_IDENTIFIER) + " " + extName + " - " + + mExt.getExtensionId().toString() + "\n"); + + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_CRITICAL)); + if (mExt.isCritical()) { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_NO) + "\n"); + } + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_VALUE) + "\n"); + sb.append(pp.toHexString(mExt.getExtensionValue(), mIndentSize + 8, 16)); + return sb.toString(); + } catch (Exception e) { + return ""; + } + + } + + /*========================================================== + * Private methods + *==========================================================*/ + + private String getNSCCommentExtension() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString( + PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString(PrettyPrintResources.TOKEN_NSC_COMMENT) + + "- " + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_CRITICAL)); + if (mExt.isCritical()) { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_NO) + "\n"); + } + sb.append(pp.indent(mIndentSize + 4) + ((NSCCommentExtension) mExt).toPrint(mIndentSize) + "\n"); + return sb.toString(); + } catch (Exception e) { + return sb.toString(); + } + } + + private String getNameConstraintsExtension() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString( + PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString(PrettyPrintResources.TOKEN_NAME_CONSTRAINTS) + + "- " + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_CRITICAL)); + if (mExt.isCritical()) { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_NO) + "\n"); + } + + sb.append(pp.indent(mIndentSize + 4) + ((NameConstraintsExtension) mExt).toPrint(mIndentSize + 4)); + + return sb.toString(); + } catch (Exception e) { + return sb.toString(); + } + } + + private String getOCSPNoCheckExtension() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString( + PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString(PrettyPrintResources.TOKEN_OCSP_NOCHECK) + + "- " + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_CRITICAL)); + if (mExt.isCritical()) { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_NO) + "\n"); + } + return sb.toString(); + } catch (Exception e) { + return sb.toString(); + } + } + + private String getSubjectInfoAccessExtension() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString( + PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString(PrettyPrintResources.TOKEN_SIA) + + "- " + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_CRITICAL)); + if (mExt.isCritical()) { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_NO) + "\n"); + } + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_ACCESS_DESC) + "\n"); + SubjectInfoAccessExtension aia = (SubjectInfoAccessExtension) mExt; + + for (int i = 0; i < aia.numberOfAccessDescription(); i++) { + AccessDescription ad = (AccessDescription) + aia.getAccessDescription(i); + ObjectIdentifier method = ad.getMethod(); + + if (method.equals(SubjectInfoAccessExtension.METHOD_OCSP)) { + sb.append(pp.indent(mIndentSize + 8) + "Method #" + i + ": " + + "ocsp" + "\n"); + } else { + sb.append(pp.indent(mIndentSize + 8) + "Method #" + i + ": " + + method.toString() + "\n"); + } + sb.append(pp.indent(mIndentSize + 8) + "Location #" + i + ": " + + ad.getLocation().toString() + "\n"); + } + return sb.toString(); + } catch (Exception e) { + return sb.toString(); + } + } + + private String getAuthInfoAccessExtension() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString( + PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString(PrettyPrintResources.TOKEN_AIA) + + "- " + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_CRITICAL)); + if (mExt.isCritical()) { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_NO) + "\n"); + } + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_ACCESS_DESC) + "\n"); + AuthInfoAccessExtension aia = (AuthInfoAccessExtension) mExt; + + for (int i = 0; i < aia.numberOfAccessDescription(); i++) { + AccessDescription ad = (AccessDescription) + aia.getAccessDescription(i); + ObjectIdentifier method = ad.getMethod(); + + if (method.equals(AuthInfoAccessExtension.METHOD_OCSP)) { + sb.append(pp.indent(mIndentSize + 8) + "Method #" + i + ": " + + "ocsp" + "\n"); + } else { + sb.append(pp.indent(mIndentSize + 8) + "Method #" + i + ": " + + method.toString() + "\n"); + } + sb.append(pp.indent(mIndentSize + 8) + "Location #" + i + ": " + + ad.getLocation().toString() + "\n"); + } + return sb.toString(); + } catch (Exception e) { + return sb.toString(); + } + } + + private String getPresenceServerExtension() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString( + PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString(PrettyPrintResources.TOKEN_PRESENCE_SERVER) + + "- " + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_CRITICAL)); + if (mExt.isCritical()) { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_NO) + "\n"); + } + + PresenceServerExtension pse = (PresenceServerExtension) mExt; + + sb.append(pp.indent(mIndentSize + 4) + "Version : " + pse.getVersion() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + "Street Address : " + pse.getStreetAddress() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + "Telephone Number : " + pse.getTelephoneNumber() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + "RFC822 Name : " + pse.getRFC822() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + "ID : " + pse.getID() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + "Host Name : " + pse.getHostName() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + "Port Number : " + pse.getPortNumber() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + "Max Users : " + pse.getMaxUsers() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + "Service Level : " + pse.getServiceLevel() + "\n"); + + return sb.toString(); + } catch (Exception e) { + return sb.toString(); + } + } + + private String getPrivateKeyUsageExtension() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString( + PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString(PrettyPrintResources.TOKEN_PRIVATE_KEY_USAGE) + + "- " + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_CRITICAL)); + if (mExt.isCritical()) { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_NO) + "\n"); + } + + PrivateKeyUsageExtension usage = (PrivateKeyUsageExtension) mExt; + + sb.append(pp.indent(mIndentSize + 4) + "Validity:\n"); + + if (dateFormater == null) { + dateFormater = DateFormat.getDateInstance(DateFormat.FULL); + } + String notBefore = dateFormater.format(usage.getNotBefore()); + String notAfter = dateFormater.format(usage.getNotAfter()); + + sb.append(pp.indent(mIndentSize + 8) + "Not Before: " + notBefore + "\n"); + sb.append(pp.indent(mIndentSize + 8) + "Not After: " + notAfter + "\n"); + + return sb.toString(); + } catch (Exception e) { + return sb.toString(); + } + } + + private String getExtendedKeyUsageExtension() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString( + PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString(PrettyPrintResources.TOKEN_EXTENDED_KEY_USAGE) + + "- " + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_CRITICAL)); + if (mExt.isCritical()) { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_NO) + "\n"); + } + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_EXTENDED_KEY_USAGE) + "\n"); + ExtendedKeyUsageExtension usage = (ExtendedKeyUsageExtension) mExt; + Enumeration e = usage.getOIDs(); + + if (e != null) { + while (e.hasMoreElements()) { + ObjectIdentifier oid = (ObjectIdentifier) e.nextElement(); + + if (oid.equals(ExtendedKeyUsageExtension.OID_OCSP_SIGNING)) { + sb.append(pp.indent(mIndentSize + 8) + "OCSPSigning" + "\n"); + } else { + sb.append(pp.indent(mIndentSize + 8) + oid.toString() + "\n"); + } + } + } + return sb.toString(); + } catch (Exception e) { + return sb.toString(); + } + } + + /** + * String Representation of KeyUsageExtension + */ + private String getKeyUsage() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString( + PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString(PrettyPrintResources.TOKEN_KEY_USAGE) + + "- " + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_CRITICAL)); + if (mExt.isCritical()) { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_NO) + "\n"); + } + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_KEY_USAGE) + "\n"); + KeyUsageExtension usage = (KeyUsageExtension) mExt; + + if (((Boolean) usage.get(KeyUsageExtension.DIGITAL_SIGNATURE)).booleanValue()) { + sb.append(pp.indent(mIndentSize + 8) + mResource.getString(KeyUsageExtension.DIGITAL_SIGNATURE) + "\n"); + } + if (((Boolean) usage.get(KeyUsageExtension.NON_REPUDIATION)).booleanValue()) { + sb.append(pp.indent(mIndentSize + 8) + mResource.getString(KeyUsageExtension.NON_REPUDIATION) + "\n"); + } + if (((Boolean) usage.get(KeyUsageExtension.KEY_ENCIPHERMENT)).booleanValue()) { + sb.append(pp.indent(mIndentSize + 8) + mResource.getString(KeyUsageExtension.KEY_ENCIPHERMENT) + "\n"); + } + if (((Boolean) usage.get(KeyUsageExtension.DATA_ENCIPHERMENT)).booleanValue()) { + sb.append(pp.indent(mIndentSize + 8) + mResource.getString(KeyUsageExtension.DATA_ENCIPHERMENT) + "\n"); + } + if (((Boolean) usage.get(KeyUsageExtension.KEY_AGREEMENT)).booleanValue()) { + sb.append(pp.indent(mIndentSize + 8) + mResource.getString(KeyUsageExtension.KEY_AGREEMENT) + "\n"); + } + if (((Boolean) usage.get(KeyUsageExtension.KEY_CERTSIGN)).booleanValue()) { + sb.append(pp.indent(mIndentSize + 8) + mResource.getString(KeyUsageExtension.KEY_CERTSIGN) + "\n"); + } + if (((Boolean) usage.get(KeyUsageExtension.CRL_SIGN)).booleanValue()) { + sb.append(pp.indent(mIndentSize + 8) + mResource.getString(KeyUsageExtension.CRL_SIGN) + "\n"); + } + if (((Boolean) usage.get(KeyUsageExtension.ENCIPHER_ONLY)).booleanValue()) { + sb.append(pp.indent(mIndentSize + 8) + mResource.getString(KeyUsageExtension.ENCIPHER_ONLY) + "\n"); + } + if (((Boolean) usage.get(KeyUsageExtension.DECIPHER_ONLY)).booleanValue()) { + sb.append(pp.indent(mIndentSize + 8) + mResource.getString(KeyUsageExtension.DECIPHER_ONLY) + "\n"); + } + return sb.toString(); + } catch (Exception e) { + return sb.toString(); + } + + } + + /** + * String Representation of NSCertTypeExtension + */ + private String getCertType() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString(PrettyPrintResources.TOKEN_CERT_TYPE) + "- " + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); + if (mExt.isCritical()) { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); + } + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_CERT_USAGE) + "\n"); + NSCertTypeExtension type = (NSCertTypeExtension) mExt; + + if (((Boolean) type.get(NSCertTypeExtension.SSL_CLIENT)).booleanValue()) { + sb.append(pp.indent(mIndentSize + 8) + mResource.getString(NSCertTypeExtension.SSL_CLIENT) + "\n"); + } + if (((Boolean) type.get(NSCertTypeExtension.SSL_SERVER)).booleanValue()) { + sb.append(pp.indent(mIndentSize + 8) + mResource.getString(NSCertTypeExtension.SSL_SERVER) + "\n"); + } + if (((Boolean) type.get(NSCertTypeExtension.EMAIL)).booleanValue()) { + sb.append(pp.indent(mIndentSize + 8) + mResource.getString(NSCertTypeExtension.EMAIL) + "\n"); + } + if (((Boolean) type.get(NSCertTypeExtension.OBJECT_SIGNING)).booleanValue()) { + sb.append(pp.indent(mIndentSize + 8) + mResource.getString(NSCertTypeExtension.OBJECT_SIGNING) + "\n"); + } + if (((Boolean) type.get(NSCertTypeExtension.SSL_CA)).booleanValue()) { + sb.append(pp.indent(mIndentSize + 8) + mResource.getString(NSCertTypeExtension.SSL_CA) + "\n"); + } + if (((Boolean) type.get(NSCertTypeExtension.EMAIL_CA)).booleanValue()) { + sb.append(pp.indent(mIndentSize + 8) + mResource.getString(NSCertTypeExtension.EMAIL_CA) + "\n"); + } + if (((Boolean) type.get(NSCertTypeExtension.OBJECT_SIGNING_CA)).booleanValue()) { + sb.append(pp.indent(mIndentSize + 8) + mResource.getString(NSCertTypeExtension.OBJECT_SIGNING_CA) + "\n"); + } + return sb.toString(); + } catch (Exception e) { + return ""; + } + + } + + /** + * String Representation of SubjectKeyIdentifierExtension + */ + private String getSubjectKeyIdentifier() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString(PrettyPrintResources.TOKEN_SKI) + "- " + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); + if (mExt.isCritical()) { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); + } + SubjectKeyIdentifierExtension id = (SubjectKeyIdentifierExtension) mExt; + KeyIdentifier keyId = (KeyIdentifier) id.get(SubjectKeyIdentifierExtension.KEY_ID); + + if (keyId != null) { + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_KEY_ID) + "\n"); + sb.append(pp.toHexString(keyId.getIdentifier(), 24, 16)); + } + return sb.toString(); + } catch (Exception e) { + return ""; + } + } + + /** + * String Representation of AuthorityKeyIdentifierExtension + */ + private String getAuthorityKeyIdentifier() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString(PrettyPrintResources.TOKEN_AKI) + "- " + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); + if (mExt.isCritical()) { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); + } + AuthorityKeyIdentifierExtension id = (AuthorityKeyIdentifierExtension) mExt; + KeyIdentifier keyId = (KeyIdentifier) id.get(AuthorityKeyIdentifierExtension.KEY_ID); + + if (keyId != null) { + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_KEY_ID) + "\n"); + sb.append(pp.toHexString(keyId.getIdentifier(), mIndentSize + 8, 16)); + // sb.append(pp.toHexString(keyId.getIdentifier(),24,16)); + } + GeneralNames authNames = (GeneralNames) id.get(AuthorityKeyIdentifierExtension.AUTH_NAME); + + if (authNames != null) { + for (int i = 0; i < authNames.size(); i++) { + GeneralName authName = (GeneralName) authNames.elementAt(i); + + if (authName != null) { + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_AUTH_NAME) + authName.toString() + "\n"); + } + } + } + + SerialNumber serial = (SerialNumber) id.get(AuthorityKeyIdentifierExtension.SERIAL_NUMBER); + + if (serial != null) { + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_SERIAL) + + "0x" + serial.getNumber().toBigInteger().toString(16).toUpperCase() + "\n"); + } + return sb.toString(); + } catch (Exception e) { + return ""; + } + } + + /** + * String Representation of CRLReasonExtension + */ + private String getCRLReasonExtension() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString(PrettyPrintResources.TOKEN_REVOCATION_REASON) + "- " + + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); + CRLReasonExtension ext = (CRLReasonExtension) mExt; + + if (((Extension) mExt).isCritical()) { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); + } + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_REASON) + + ext.getReason().toString() + "\n"); + + return sb.toString(); + } catch (Exception e) { + return ""; + } + } + + /** + * String Representation of InhibitAnyPolicyExtension + */ + private String getInhibitAnyPolicyExtension() { + StringBuffer sb = new StringBuffer(); + try { + sb.append(pp.indent(mIndentSize) + + mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_INHIBIT_ANY_POLICY_EXT) + "- "+ + mExt.getExtensionId().toString() +"\n"); + sb.append(pp.indent(mIndentSize + 4) + + mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); + InhibitAnyPolicyExtension ext = (InhibitAnyPolicyExtension)mExt; + if (((Extension) mExt).isCritical()) + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_YES) + "\n"); + else + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_NO) + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_SKIP_CERTS)); + BigInt num = ext.getSkipCerts(); + sb.append(""+num.toInt() + "\n"); + return sb.toString(); + } catch (Exception e) { + return ""; + } + } + + /** + * String Representation of BasicConstraintsExtension + */ + private String getBasicConstraintsExtension() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString(PrettyPrintResources.TOKEN_BASIC_CONSTRAINTS) + "- " + + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); + BasicConstraintsExtension ext = (BasicConstraintsExtension) mExt; + + if (((Extension) mExt).isCritical()) { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); + } + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_IS_CA)); + boolean isCA = ((Boolean) ext.get(BasicConstraintsExtension.IS_CA)).booleanValue(); + + if (isCA) { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); + } + Integer pathLength = (Integer) ext.get(BasicConstraintsExtension.PATH_LEN); + + if (pathLength != null) { + if (pathLength.longValue() >= 0) { + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_PATH_LEN) + + pathLength.toString() + "\n"); + } else if (pathLength.longValue() == -1 || pathLength.longValue() == -2) { + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_PATH_LEN) + + mResource.getString(PrettyPrintResources.TOKEN_PATH_LEN_UNLIMITED) + "\n"); + } else { + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_PATH_LEN) + + mResource.getString(PrettyPrintResources.TOKEN_PATH_LEN_INVALID) + + " (" + pathLength.toString() + ")\n"); + } + } + + return sb.toString(); + } catch (Exception e) { + return ""; + } + } + + /** + * String Representation of CRLNumberExtension + */ + private String getCRLNumberExtension() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString(PrettyPrintResources.TOKEN_CRL_NUMBER) + "- " + + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); + CRLNumberExtension ext = (CRLNumberExtension) mExt; + + if (((Extension) mExt).isCritical()) { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); + } + BigInteger crlNumber = (BigInteger) ext.get(CRLNumberExtension.NUMBER); + + if (crlNumber != null) { + sb.append(pp.indent(mIndentSize + 4) + + mResource.getString(PrettyPrintResources.TOKEN_NUMBER) + + crlNumber.toString() + "\n"); + } + + return sb.toString(); + } catch (Exception e) { + return ""; + } + } + + /** + * String Representation of DeltaCRLIndicatorExtension + */ + private String getDeltaCRLIndicatorExtension() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString(PrettyPrintResources.TOKEN_DELTA_CRL_INDICATOR) + "- " + + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); + DeltaCRLIndicatorExtension ext = (DeltaCRLIndicatorExtension) mExt; + + if (((Extension) mExt).isCritical()) { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); + } + BigInteger crlNumber = (BigInteger) ext.get(DeltaCRLIndicatorExtension.NUMBER); + + if (crlNumber != null) { + sb.append(pp.indent(mIndentSize + 4) + + mResource.getString(PrettyPrintResources.TOKEN_BASE_CRL_NUMBER) + + crlNumber.toString() + "\n"); + } + + return sb.toString(); + } catch (Exception e) { + return ""; + } + } + + /** + * String Representation of IssuerAlternativeName Extension + */ + private String getIssuerAlternativeNameExtension() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString(PrettyPrintResources.TOKEN_ISSUER_ALT_NAME) + "- " + + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); + IssuerAlternativeNameExtension ext = (IssuerAlternativeNameExtension) mExt; + + if (((Extension) mExt).isCritical()) { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); + } + + GeneralNames issuerNames = (GeneralNames) ext.get(IssuerAlternativeNameExtension.ISSUER_NAME); + + if (issuerNames != null) { + sb.append(pp.indent(mIndentSize + 4) + + mResource.getString(PrettyPrintResources.TOKEN_ISSUER_NAMES) + "\n"); + for (int i = 0; i < issuerNames.size(); i++) { + GeneralName issuerName = (GeneralName) issuerNames.elementAt(i); + + if (issuerName != null) { + String nameType = ""; + + if (issuerName.getType() == GeneralNameInterface.NAME_DIRECTORY) + nameType = "DirectoryName: "; + sb.append(pp.indent(mIndentSize + 8) + nameType + issuerName.toString() + "\n"); + } + } + } + + return sb.toString(); + } catch (Exception e) { + return ""; + } + } + + /** + * String Representation of SubjectAlternativeName Extension + */ + private String getSubjectAlternativeNameExtension() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString(PrettyPrintResources.TOKEN_SUBJECT_ALT_NAME) + "- " + + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); + SubjectAlternativeNameExtension ext = (SubjectAlternativeNameExtension) mExt; + + if (((Extension) mExt).isCritical()) { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); + } + + GeneralNames subjectNames = (GeneralNames) ext.get(SubjectAlternativeNameExtension.SUBJECT_NAME); + + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_VALUE) + "\n"); + for (int i = 0; i < subjectNames.size(); i++) { + GeneralName subjectName = (GeneralName) subjectNames.elementAt(i); + + if (subjectName != null) { + String nameType = ""; + + if (subjectName.getType() == GeneralNameInterface.NAME_DIRECTORY) + nameType = "DirectoryName: "; + sb.append(pp.indent(mIndentSize + 8) + nameType + subjectName.toString() + "\n"); + } + } + + return sb.toString(); + } catch (Exception e) { + return ""; + } + } + + /** + * String Representation of CertificateScopeOfUse Extension + */ + private String getCertificateScopeOfUseExtension() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString(PrettyPrintResources.TOKEN_CERT_SCOPE_OF_USE) + "- " + + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); + CertificateScopeOfUseExtension ext = (CertificateScopeOfUseExtension) mExt; + + if (((Extension) mExt).isCritical()) { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); + } + Vector entries = ext.getCertificateScopeEntries(); + + if (entries != null) { + sb.append(pp.indent(mIndentSize + 4) + + mResource.getString(PrettyPrintResources.TOKEN_SCOPE_OF_USE) + "\n"); + for (int i = 0; i < entries.size(); i++) { + CertificateScopeEntry se = (CertificateScopeEntry) entries.elementAt(i); + GeneralName gn = se.getGeneralName(); + + if (gn != null) { + String nameType = ""; + + if (gn.getType() == GeneralNameInterface.NAME_DIRECTORY) + nameType = "DirectoryName: "; + sb.append(pp.indent(mIndentSize + 8) + nameType + gn.toString() + "\n"); + } + BigInt port = se.getPort(); + + if (port != null) { + sb.append(pp.indent(mIndentSize + 8) + PrettyPrintResources.TOKEN_PORT + + port.toBigInteger().toString() + "\n"); + } + } + } + return sb.toString(); + } catch (Exception e) { + return ""; + } + } + + /** + * String Representation of FreshestCRLExtension + */ + private String getFreshestCRLExtension() { + StringBuffer sb = new StringBuffer(); + + try { + + // + // Generic stuff: name, OID, criticality + // + sb.append(pp.indent(mIndentSize) + + mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_FRESHEST_CRL_EXT) + "- " + + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + + mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); + if (((Extension) mExt).isCritical()) { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_NO) + "\n"); + } + + // + // Now the CRLDP-specific stuff + // + FreshestCRLExtension ext = (FreshestCRLExtension) mExt; + + int numPoints = ext.getNumPoints(); + + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_CRLDP_NUMPOINTS) + + numPoints + "\n"); + + for (int i = 0; i < numPoints; i++) { + + // + // print one individual CRL distribution point + // + + int idt; + + idt = mIndentSize + 4; // reset each time through loop + boolean isEmpty = true; + + sb.append(pp.indent(idt) + + mResource.getString(PrettyPrintResources.TOKEN_CRLDP_POINTN) + + i + "\n"); + + CRLDistributionPoint pt = ext.getPointAt(i); + + idt += 4; // further indent rest of information + + if (pt.getFullName() != null) { + isEmpty = false; + sb.append(pp.indent(idt) + + mResource.getString(PrettyPrintResources.TOKEN_CRLDP_DISTPOINT) + + pt.getFullName() + "\n"); + } + + if (pt.getRelativeName() != null) { + isEmpty = false; + sb.append(pp.indent(idt) + + mResource.getString(PrettyPrintResources.TOKEN_CRLDP_DISTPOINT) + + pt.getRelativeName() + "\n"); + } + + if (pt.getReasons() != null) { + isEmpty = false; + byte[] reasonBits = pt.getReasons().toByteArray(); + String reasonList = reasonBitsToReasonList(reasonBits); + + sb.append(pp.indent(idt) + + mResource.getString(PrettyPrintResources.TOKEN_CRLDP_REASONS) + + reasonList + "\n"); + } + + if (pt.getCRLIssuer() != null) { + isEmpty = false; + sb.append(pp.indent(idt) + + mResource.getString(PrettyPrintResources.TOKEN_CRLDP_CRLISSUER) + + pt.getCRLIssuer() + "\n"); + } + + if (isEmpty) { + sb.append(pp.indent(idt) + "empty\n"); + } + + } + + return sb.toString(); + } catch (Exception e) { + return ""; + } + } + + /** + * String Representation of CRLDistributionPointsExtension + */ + private String getCRLDistributionPointsExtension() { + StringBuffer sb = new StringBuffer(); + + try { + + // + // Generic stuff: name, OID, criticality + // + sb.append(pp.indent(mIndentSize) + + mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_CRL_DP_EXT) + "- " + + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + + mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); + if (((Extension) mExt).isCritical()) { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_NO) + "\n"); + } + + // + // Now the CRLDP-specific stuff + // + CRLDistributionPointsExtension ext = + (CRLDistributionPointsExtension) mExt; + + int numPoints = ext.getNumPoints(); + + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_CRLDP_NUMPOINTS) + + numPoints + "\n"); + + for (int i = 0; i < numPoints; i++) { + + // + // print one individual CRL distribution point + // + + int idt; + + idt = mIndentSize + 4; // reset each time through loop + boolean isEmpty = true; + + sb.append(pp.indent(idt) + + mResource.getString(PrettyPrintResources.TOKEN_CRLDP_POINTN) + + i + "\n"); + + CRLDistributionPoint pt = ext.getPointAt(i); + + idt += 4; // further indent rest of information + + if (pt.getFullName() != null) { + isEmpty = false; + sb.append(pp.indent(idt) + + mResource.getString(PrettyPrintResources.TOKEN_CRLDP_DISTPOINT) + + pt.getFullName() + "\n"); + } + + if (pt.getRelativeName() != null) { + isEmpty = false; + sb.append(pp.indent(idt) + + mResource.getString(PrettyPrintResources.TOKEN_CRLDP_DISTPOINT) + + pt.getRelativeName() + "\n"); + } + + if (pt.getReasons() != null) { + isEmpty = false; + byte[] reasonBits = pt.getReasons().toByteArray(); + String reasonList = reasonBitsToReasonList(reasonBits); + + sb.append(pp.indent(idt) + + mResource.getString(PrettyPrintResources.TOKEN_CRLDP_REASONS) + + reasonList + "\n"); + } + + if (pt.getCRLIssuer() != null) { + isEmpty = false; + sb.append(pp.indent(idt) + + mResource.getString(PrettyPrintResources.TOKEN_CRLDP_CRLISSUER) + + pt.getCRLIssuer() + "\n"); + } + + if (isEmpty) { + sb.append(pp.indent(idt) + "empty\n"); + } + + } + + return sb.toString(); + } catch (Exception e) { + return ""; + } + } + + private static String reasonBitsToReasonList(byte[] reasonBits) { + + Reason[] reasons = Reason.bitArrayToReasonArray(reasonBits); + + if (reasons.length == 0) { + return ""; + } else { + StringBuffer buf = new StringBuffer(); + + buf.append(reasons[0].getName()); + for (int i = 1; i < reasons.length; i++) { + buf.append(", "); + buf.append(reasons[i].getName()); + } + return buf.toString(); + } + } + + /** + * String Representation of IssuerAlternativeName Extension + */ + private String getIssuingDistributionPointExtension() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString(PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString(PrettyPrintResources.TOKEN_ISSUING_DIST_POINT) + "- " + + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); + if (((Extension) mExt).isCritical()) { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); + } + + IssuingDistributionPointExtension ext = (IssuingDistributionPointExtension) mExt; + IssuingDistributionPoint issuingDistributionPoint = ext.getIssuingDistributionPoint(); + + if (issuingDistributionPoint != null) { + GeneralNames fullNames = issuingDistributionPoint.getFullName(); + RDN relativeName = issuingDistributionPoint.getRelativeName(); + + if (fullNames != null || relativeName != null) { + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_DIST_POINT_NAME) + "\n"); + if (fullNames != null) { + sb.append(pp.indent(mIndentSize + 8) + mResource.getString(PrettyPrintResources.TOKEN_FULL_NAME) + "\n"); + for (int i = 0; i < fullNames.size(); i++) { + GeneralName fullName = (GeneralName) fullNames.elementAt(i); + + if (fullName != null) { + sb.append(pp.indent(mIndentSize + 12) + fullName.toString() + "\n"); + } + } + } + if (relativeName != null) { + sb.append(pp.indent(mIndentSize + 8) + mResource.getString(PrettyPrintResources.TOKEN_RELATIVE_NAME) + + relativeName.toString() + "\n"); + } + } + + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_ONLY_USER_CERTS)); + if (issuingDistributionPoint.getOnlyContainsUserCerts()) { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); + } + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_ONLY_CA_CERTS)); + if (issuingDistributionPoint.getOnlyContainsCACerts()) { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); + } + + BitArray onlySomeReasons = issuingDistributionPoint.getOnlySomeReasons(); + + if (onlySomeReasons != null) { + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_ONLY_SOME_REASONS)); + sb.append("0x" + pp.toHexString(onlySomeReasons.toByteArray())); + } + + sb.append(pp.indent(mIndentSize + 4) + mResource.getString(PrettyPrintResources.TOKEN_INDIRECT_CRL)); + if (issuingDistributionPoint.getIndirectCRL()) { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); + } + } + + return sb.toString(); + } catch (Exception e) { + return ""; + } + } + + /** + * String Representation of InvalidityDateExtension + */ + private String getInvalidityDateExtension() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString( + PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString(PrettyPrintResources.TOKEN_INVALIDITY_DATE) + + "- " + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_CRITICAL)); + InvalidityDateExtension ext = (InvalidityDateExtension) mExt; + + if (((Extension) mExt).isCritical()) { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); + } + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_DATE_OF_INVALIDITY) + + ext.getInvalidityDate().toString() + "\n"); + return sb.toString(); + } catch (Exception e) { + return ""; + } + } + + /** + * String Representation of CertificateIssuerExtension + */ + private String getCertificateIssuerExtension() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString( + PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString(PrettyPrintResources.TOKEN_CERTIFICATE_ISSUER) + + "- " + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_CRITICAL)); + CertificateIssuerExtension ext = (CertificateIssuerExtension) mExt; + + if (((Extension) mExt).isCritical()) { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); + } + + GeneralNames issuerNames = (GeneralNames) ext.get( + CertificateIssuerExtension.CERTIFICATE_ISSUER); + + if (issuerNames != null) { + sb.append(pp.indent(mIndentSize + 4) + + mResource.getString(PrettyPrintResources.TOKEN_ISSUER_NAMES) + "\n"); + for (int i = 0; i < issuerNames.size(); i++) { + GeneralName issuerName = (GeneralName) issuerNames.elementAt(i); + + if (issuerName != null) { + String nameType = ""; + + if (issuerName.getType() == GeneralNameInterface.NAME_DIRECTORY) + nameType = "DirectoryName: "; + sb.append(pp.indent(mIndentSize + 8) + nameType + issuerName.toString() + "\n"); + } + } + } + + return sb.toString(); + } catch (Exception e) { + return ""; + } + } + + /** + * String Representation of HoldInstructionExtension + */ + private String getHoldInstructionExtension() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString( + PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString(PrettyPrintResources.TOKEN_HOLD_INSTRUCTION) + + "- " + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_CRITICAL)); + HoldInstructionExtension ext = (HoldInstructionExtension) mExt; + + if (((Extension) mExt).isCritical()) { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); + } + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_HOLD_INSTRUCTION_CODE) + + ext.getHoldInstructionCodeDescription() + "\n"); + return sb.toString(); + } catch (Exception e) { + return ""; + } + } + + /** + * String Representation of PolicyConstraintsExtension + */ + private String getPolicyConstraintsExtension() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString( + PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append( + mResource.getString( + PrettyPrintResources.TOKEN_POLICY_CONSTRAINTS) + + "- " + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_CRITICAL)); + if (((Extension) mExt).isCritical()) { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); + } + + PolicyConstraintsExtension ext = (PolicyConstraintsExtension) mExt; + int require = ext.getRequireExplicitMapping(); + int inhibit = ext.getInhibitPolicyMapping(); + + sb.append( + pp.indent(mIndentSize + 4) + + mResource.getString( + PrettyPrintResources.TOKEN_REQUIRE_EXPLICIT_POLICY) + + ((require == -1) ? + mResource.getString(PrettyPrintResources.TOKEN_NOT_SET) : + String.valueOf(require)) + "\n"); + sb.append( + pp.indent(mIndentSize + 4) + + mResource.getString( + PrettyPrintResources.TOKEN_INHIBIT_POLICY_MAPPING) + + ((inhibit == -1) ? + mResource.getString(PrettyPrintResources.TOKEN_NOT_SET) : + String.valueOf(inhibit)) + "\n"); + return sb.toString(); + } catch (Exception e) { + return ""; + } + } + + /** + * String Representation of PolicyMappingsExtension + */ + private String getPolicyMappingsExtension() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString( + PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString(PrettyPrintResources.TOKEN_POLICY_MAPPINGS) + + "- " + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_CRITICAL)); + if (((Extension) mExt).isCritical()) { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); + } + + PolicyMappingsExtension ext = (PolicyMappingsExtension) mExt; + Enumeration maps = ext.getMappings(); + + sb.append(pp.indent(mIndentSize + 4) + + mResource.getString(PrettyPrintResources.TOKEN_MAPPINGS)); + if (maps == null || !maps.hasMoreElements()) { + sb.append( + mResource.getString(PrettyPrintResources.TOKEN_NONE) + "\n"); + } else { + sb.append("\n"); + for (int i = 0; maps.hasMoreElements(); i++) { + sb.append(pp.indent(mIndentSize + 8) + + mResource.getString( + PrettyPrintResources.TOKEN_MAP) + i + ":" + "\n"); + CertificatePolicyMap m = + (CertificatePolicyMap) maps.nextElement(); + + sb.append(pp.indent(mIndentSize + 12) + + mResource.getString( + PrettyPrintResources.TOKEN_ISSUER_DOMAIN_POLICY) + + m.getIssuerIdentifier().getIdentifier().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 12) + + mResource.getString( + PrettyPrintResources.TOKEN_SUBJECT_DOMAIN_POLICY) + + m.getSubjectIdentifier().getIdentifier().toString() + "\n"); + } + } + return sb.toString(); + } catch (Throwable e) { + return ""; + } + } + + /** + * String Representation of SubjectDirAttributesExtension + */ + private String getSubjectDirAttributesExtension() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString( + PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString(PrettyPrintResources.TOKEN_SUBJECT_DIR_ATTR) + + "- " + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + + mResource.getString(PrettyPrintResources.TOKEN_CRITICAL)); + if (((Extension) mExt).isCritical()) { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n"); + } + + SubjectDirAttributesExtension ext = + (SubjectDirAttributesExtension) mExt; + + sb.append(pp.indent(mIndentSize + 4) + + mResource.getString(PrettyPrintResources.TOKEN_ATTRIBUTES)); + Enumeration attrs = ext.getAttributesList(); + + if (attrs == null || !attrs.hasMoreElements()) { + sb.append( + mResource.getString(PrettyPrintResources.TOKEN_NONE) + "\n"); + } else { + sb.append("\n"); + for (int j = 0; attrs.hasMoreElements(); j++) { + Attribute attr = (Attribute) attrs.nextElement(); + + sb.append(pp.indent(mIndentSize + 8) + + mResource.getString( + PrettyPrintResources.TOKEN_ATTRIBUTE) + j + ":" + "\n"); + sb.append(pp.indent(mIndentSize + 12) + + mResource.getString( + PrettyPrintResources.TOKEN_IDENTIFIER) + + attr.getOid().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 12) + + mResource.getString( + PrettyPrintResources.TOKEN_VALUES)); + Enumeration values = attr.getValues(); + + if (values == null || !values.hasMoreElements()) { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_NONE) + "\n"); + } else { + for (int k = 0; values.hasMoreElements(); k++) { + String v = (String) values.nextElement(); + + if (k != 0) + sb.append(","); + sb.append(v); + } + } + sb.append("\n"); + } + } + return sb.toString(); + } catch (Throwable e) { + return ""; + } + } + + private String getCertificatePoliciesExtension() { + StringBuffer sb = new StringBuffer(); + + try { + sb.append(pp.indent(mIndentSize) + mResource.getString( + PrettyPrintResources.TOKEN_IDENTIFIER)); + sb.append(mResource.getString(PrettyPrintResources.TOKEN_CERT_POLICIES) + + "- " + mExt.getExtensionId().toString() + "\n"); + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_CRITICAL)); + if (mExt.isCritical()) { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_YES) + "\n"); + } else { + sb.append(mResource.getString( + PrettyPrintResources.TOKEN_NO) + "\n"); + } + sb.append(pp.indent(mIndentSize + 4) + mResource.getString( + PrettyPrintResources.TOKEN_CERT_POLICIES) + "\n"); + CertificatePoliciesExtension cp = (CertificatePoliciesExtension) mExt; + Vector cpv = (Vector) cp.get("infos"); + Enumeration e = cpv.elements(); + + if (e != null) { + while (e.hasMoreElements()) { + CertificatePolicyInfo cpi = (CertificatePolicyInfo) e.nextElement(); + + sb.append(pp.indent(mIndentSize + 8) + cpi.getPolicyIdentifier().getIdentifier().toString() + "\n"); + } + } + return sb.toString(); + } catch (Exception e) { + return sb.toString(); + } + } + + +} + diff --git a/pki/base/util/src/netscape/security/util/ObjectIdentifier.java b/pki/base/util/src/netscape/security/util/ObjectIdentifier.java new file mode 100644 index 000000000..fed9637af --- /dev/null +++ b/pki/base/util/src/netscape/security/util/ObjectIdentifier.java @@ -0,0 +1,447 @@ +// --- 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.util; + +import java.io.*; +import java.util.StringTokenizer; + + +/** + * Represent an ISO Object Identifier. + * + *

Object Identifiers are arbitrary length hierarchical identifiers. + * The individual components are numbers, and they define paths from the + * root of an ISO-managed identifier space. You will sometimes see a + * string name used instead of (or in addition to) the numerical id. + * These are synonyms for the numerical IDs, but are not widely used + * since most sites do not know all the requisite strings, while all + * sites can parse the numeric forms. + * + *

So for example, JavaSoft has the sole authority to assign the + * meaning to identifiers below the 1.3.6.1.4.42.2.17 node in the + * hierarchy, and other organizations can easily acquire the ability + * to assign such unique identifiers. + * + * @version 1.23 + * + * @author David Brownell + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +final public +class ObjectIdentifier implements Serializable +{ + /** use serialVersionUID from JDK 1.1. for interoperability */ + private static final long serialVersionUID = 8697030238860181294L; + + /** + * Constructs an object identifier from a string. This string + * should be of the form 1.23.34.45.56 etc. + */ + public ObjectIdentifier (String oid) + { + if (oid == null) + return; + + int ch = '.'; + int start = 0; + int end = 0; + + // Calculate length of oid + componentLen = 0; + while ((end = oid.indexOf(ch,start)) != -1) { + start = end + 1; + componentLen += 1; + } + componentLen += 1; + components = new int[componentLen]; + + start = 0; + int i = 0; + String comp = null; + while ((end = oid.indexOf(ch,start)) != -1) { + comp = oid.substring(start,end); + components[i++] = Integer.valueOf(comp).intValue(); + start = end + 1; + } + comp = oid.substring(start); + components[i] = Integer.valueOf(comp).intValue(); + } + + /** + * Constructs an object ID from an array of integers. This + * is used to construct constant object IDs. + */ + public ObjectIdentifier (int values []) + { + try { + components = (int []) values.clone (); + componentLen = values.length; + } catch (Throwable t) { + System.out.println ("X509.ObjectIdentifier(), no cloning!"); + } + } + + + /** + * Constructs an object ID from an ASN.1 encoded input stream. + * The encoding of the ID in the stream uses "DER", a BER/1 subset. + * In this case, that means a triple { typeId, length, data }. + * + *

NOTE: When an exception is thrown, the + * input stream has not been returned to its "initial" state. + * + * @param in DER-encoded data holding an object ID + * @exception IOException indicates a decoding error + */ + public ObjectIdentifier (DerInputStream in) + throws IOException + { + byte type_id; + int bufferEnd; + + /* + * Object IDs are a "universal" type, and their tag needs only + * one byte of encoding. Verify that the tag of this datum + * is that of an object ID. + * + * Then get and check the length of the ID's encoding. We set + * up so that we can use in.available() to check for the end of + * this value in the data stream. + */ + type_id = (byte) in.getByte (); + if (type_id != DerValue.tag_ObjectId) + throw new IOException ( + "X509.ObjectIdentifier() -- data isn't an object ID" + + " (tag = " + type_id + ")" + ); + + bufferEnd = in.available () - in.getLength () - 1; + if (bufferEnd < 0) + throw new IOException ( + "X509.ObjectIdentifier() -- not enough data"); + + initFromEncoding (in, bufferEnd); + } + + /* + * Build the OID from the rest of a DER input buffer; the tag + * and length have been removed/verified + */ + ObjectIdentifier (DerInputBuffer buf) throws IOException + { + initFromEncoding (new DerInputStream (buf), 0); + } + + /* + * Helper function -- get the OID from a stream, after tag and + * length are verified. + */ + private void initFromEncoding (DerInputStream in, int bufferEnd) + throws IOException + { + + /* + * Now get the components ("sub IDs") one at a time. We fill a + * temporary buffer, resizing it as needed. + */ + int component; + boolean first_subid = true; + + for (components = new int [allocationQuantum], componentLen = 0; + in.available () > bufferEnd; + ) { + component = getComponent (in); + + if (first_subid) { + int X, Y; + + /* + * The ISO root has three children (0, 1, 2) and those nodes + * aren't allowed to assign IDs larger than 39. These rules + * are memorialized by some special casing in the BER encoding + * of object IDs ... or maybe it's vice versa. + * + * NOTE: the allocation quantum is large enough that we know + * we don't have to reallocate here! + */ + if (component < 40) + X = 0; + else if (component < 80) + X = 1; + else + X = 2; + Y = component - ( X * 40); + + components [0] = X; + components [1] = Y; + componentLen = 2; + + first_subid = false; + + } else { + + /* + * Other components are encoded less exotically. The only + * potential trouble is the need to grow the array. + */ + if (componentLen >= components.length) { + int tmp_components []; + + tmp_components = new int [components.length + + allocationQuantum]; + System.arraycopy (components, 0, tmp_components, 0, + components.length); + components = tmp_components; + } + components [componentLen++] = component; + } + } + + /* + * Final sanity check -- if we didn't use exactly the number of bytes + * specified, something's quite wrong. + */ + if (in.available () != bufferEnd) { + throw new IOException ( + "X509.ObjectIdentifier() -- malformed input data"); + } + } + + + /* + * n.b. the only public interface is DerOutputStream.putOID() + */ + void encode (DerOutputStream out) throws IOException + { + DerOutputStream bytes = new DerOutputStream (); + int i; + + bytes.write ((components [0] * 40) + components [1]); + for (i = 2; i < componentLen; i++) + putComponent (bytes, components [i]); + + /* + * Now that we've constructed the component, encode + * it in the stream we were given. + */ + out.write (DerValue.tag_ObjectId, bytes); + } + + /* + * Tricky OID component parsing technique ... note that one bit + * per octet is lost, this returns at most 28 bits of component. + * Also, notice this parses in big-endian format. + */ + private static int getComponent (DerInputStream in) + throws IOException + { + int retval, i, tmp; + + for (i = 0, retval = 0; i < 4; i++) { + retval <<= 7; + tmp = in.getByte (); + retval |= (tmp & 0x07f); + if ((tmp & 0x080) == 0) + return retval; + } + + throw new IOException ("X509.OID, component value too big"); + } + + /* + * Reverse of the above routine. Notice it needs to emit in + * big-endian form, so it buffers the output until it's ready. + * (Minimum length encoding is a DER requirement.) + */ + private static void putComponent (DerOutputStream out, int val) + throws IOException + { + int i; + byte buf [] = new byte [4] ; + + for (i = 0; i < 4; i++) { + buf [i] = (byte) (val & 0x07f); + val >>>= 7; + if (val == 0) + break; + } + for ( ; i > 0; --i) + out.write (buf [i] | 0x080); + out.write (buf [0]); + } + + // XXX this API should probably facilitate the JDK sort utility + + /** + * Compares this identifier with another, for sorting purposes. + * An identifier does not precede itself. + * + * @param other identifer that may precede this one. + * @return true iff other precedes this one + * in a particular sorting order. + */ + public boolean precedes (ObjectIdentifier other) + { + int i; + + // shorter IDs go first + if (other == this || componentLen < other.componentLen) + return false; + if (other.componentLen < componentLen) + return true; + + // for each component, the lesser component goes first + for (i = 0; i < componentLen; i++) { + if (other.components [i] < components [i]) + return true; + } + + // identical IDs don't precede each other + return false; + } + + public boolean equals (Object other) + { + if (other instanceof ObjectIdentifier) + return equals ((ObjectIdentifier) other); + else + return false; + } + + /** + * Compares this identifier with another, for equality. + * + * @return true iff the names are identical. + */ + public boolean equals (ObjectIdentifier other) + { + int i; + + if (other == this) + return true; + if (componentLen != other.componentLen) + return false; + for (i = 0; i < componentLen; i++) { + if (components [i] != other.components [i]) + return false; + } + return true; + } + + public int hashCode() { + int h=0; + int oflow=0; + + for (int i=0;i> 23; + h <<= 9; + h += components[i]; + h ^= oflow; + } + return h; + } + + /** + * Returns a string form of the object ID. The format is the + * conventional "dot" notation for such IDs, without any + * user-friendly descriptive strings, since those strings + * will not be understood everywhere. + */ + public String toString () + { + String retval; + int i; + + for (i = 0, retval = ""; i < componentLen; i++) { + if (i != 0) + retval += "."; + retval += components [i]; + } + return retval; + } + + /* + * To simplify, we assume no individual component of an object ID is + * larger than 32 bits. Then we represent the path from the root as + * an array that's (usually) only filled at the beginning. + */ + private int components []; // path from root + private int componentLen; // how much is used. + + private static final int allocationQuantum = 5; // >= 2 + + /** + * Netscape Enhancement: + * This function implements a object identifier factory. It + * should help reduces in-memory Object Identifier object. + * This function also provide additional checking on the OID. + * A valid OID should start with 0, 1, or 2. + * + * Notes: + * This function never returns null. IOException is raised + * in error conditions. + */ + public static java.util.Hashtable mOIDs = new java.util.Hashtable(); + public static ObjectIdentifier getObjectIdentifier(String oid) + throws IOException + { + int value; + + if (oid == null) + throw new IOException("empty object identifier"); + + oid = oid.trim(); + + ObjectIdentifier thisOID = (ObjectIdentifier)mOIDs.get(oid); + if (thisOID != null) + return thisOID; + + StringTokenizer token = new StringTokenizer(oid, "."); + value = new Integer(token.nextToken()).intValue(); + /* First token should be 0, 1, 2 */ + if (value >= 0 && value <= 2) { + value = new Integer(token.nextToken()).intValue(); + /* Second token should be 0 <= && >= 39 */ + if (value >= 0 && value <= 39) { + thisOID = new ObjectIdentifier(oid); + if (thisOID.toString().equals(oid)) { + mOIDs.put(oid, thisOID); + return thisOID; + } + throw new IOException("invalid oid " + oid); + } else + throw new IOException("invalid oid " + oid); + } else + throw new IOException("invalid oid " + oid); + } + + public static ObjectIdentifier getObjectIdentifier(int values[]) + throws IOException + { + String retval; + int i; + + for (i = 0, retval = ""; i < values.length; i++) { + if (i != 0) + retval += "."; + retval += values [i]; + } + return getObjectIdentifier(retval); + } +} diff --git a/pki/base/util/src/netscape/security/util/PrettyPrintFormat.java b/pki/base/util/src/netscape/security/util/PrettyPrintFormat.java new file mode 100644 index 000000000..8487fa898 --- /dev/null +++ b/pki/base/util/src/netscape/security/util/PrettyPrintFormat.java @@ -0,0 +1,170 @@ +// --- 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.util; + +import java.io.*; +import java.util.*; +import java.text.*; + +/** + * This class will display the certificate content in predefined + * format. + * + * @author Andrew Wnuk + * @version $Revision: 14564 $, $Date: 2007-05-01 10:40:13 -0700 (Tue, 01 May 2007) $ + */ +public class PrettyPrintFormat { + + /*========================================================== + * variables + *==========================================================*/ + private String mSeparator = ""; + private int mIndentSize = 0; + private int mLineLen = 0; + + /*========================================================== + * constants + * + *==========================================================*/ + private final static String spaces= + " "+ + " "+ + " "+ + " "+ + " "; + + /*========================================================== + * constructors + *==========================================================*/ + + public PrettyPrintFormat(String separator) + { + mSeparator = separator; + } + + public PrettyPrintFormat(String separator, int lineLen) + { + mSeparator = separator; + mLineLen = lineLen; + } + + public PrettyPrintFormat(String separator, int lineLen, int indentSize) + { + mSeparator = separator; + mLineLen = lineLen; + mIndentSize = indentSize; + } + + /*========================================================== + * Private methods + *==========================================================*/ + + + /*========================================================== + * public methods + *==========================================================*/ + + /** + * Provide white space indention + * stevep - speed improvements. Factor of 10 improvement + * @param numSpace number of white space to be returned + * @return white spaces + */ + public String indent(int size) { + return spaces.substring(0,size); + } + + private static final char[] hexdigits = { + '0','1','2','3','4','5','6','7','8','9', + 'A','B','C','D','E','F' + }; + + + /** + * Convert Byte Array to Hex String Format + * stevep - speedup by factor of 8 + * @param byte array of data to hexify + * @param indentSize number of spaces to prepend before each line + * @param lineLen number of bytes to output on each line (0 + means: put everything on one line + * @param separator the first character of this string will be used as + the separator between bytes. + * @return string representation + */ + + public String toHexString(byte[] in, int indentSize, + int lineLen, String separator) + { + StringBuffer sb = new StringBuffer(); + int hexCount = 0; + char c[]; + int j=0; + + if (lineLen ==0) { + c = new char[in.length*3+1]; + } + else { + c = new char[lineLen*3+1]; + } + + char sep = separator.charAt(0); + + sb.append(indent(indentSize)); + for (int i = 0; i < in.length; i++) { + if (lineLen > 0 && hexCount == lineLen) { + c[j++] = '\n'; + sb.append(c,0,j); + sb.append(indent(indentSize)); + hexCount =0; + j=0; + } + byte x = in[i]; + + // output hex digits to buffer + c[j++] = hexdigits[(char) ((x >> 4) & 0xf)]; + c[j++] = hexdigits[(char) (x&0xf)]; + + // if not last char, output separator + if (i != in.length - 1) { + c[j++] = sep; + } + + hexCount++; + } + if (j>0) { + c[j++] = '\n'; + sb.append(c,0,j); + } +// sb.append("\n"); + + return sb.toString(); + } + + + public String toHexString(byte[] in, int indentSize, int lineLen) { + return toHexString(in,indentSize,lineLen,mSeparator); + } + + public String toHexString(byte[] in, int indentSize) { + return toHexString(in,indentSize,mLineLen); + } + + public String toHexString(byte[] in) { + return toHexString(in,mIndentSize); + } +} diff --git a/pki/base/util/src/netscape/security/util/PrettyPrintResources.java b/pki/base/util/src/netscape/security/util/PrettyPrintResources.java new file mode 100644 index 000000000..062592a55 --- /dev/null +++ b/pki/base/util/src/netscape/security/util/PrettyPrintResources.java @@ -0,0 +1,300 @@ +// --- 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.util; + + +import java.util.*; +import netscape.security.x509.*; +import netscape.security.extensions.*; + + +/** + * Resource Boundle for the Pretty Print + * + * @author Jack Pan-Chen + * @version $Revision: 14564 $, $Date: 2007-05-01 10:40:13 -0700 (Tue, 01 May 2007) $ + */ + +public class PrettyPrintResources extends ListResourceBundle { + + /** + * Returns content + */ + public Object[][] getContents() { + return contents; + } + + /** + * Constants. The suffix represents the number of + * possible parameters. + */ + + //certificate pretty print + public final static String TOKEN_CERTIFICATE = "tokenCertificate"; + public final static String TOKEN_DATA = "tokenData"; + public final static String TOKEN_VERSION = "tokenVersion"; + public final static String TOKEN_SERIAL = "tokenSerial"; + public final static String TOKEN_SIGALG = "tokenSignatureAlgorithm"; + public final static String TOKEN_ISSUER = "tokenIssuer"; + public final static String TOKEN_VALIDITY = "tokenValidity"; + public final static String TOKEN_NOT_BEFORE = "tokenNotBefore"; + public final static String TOKEN_NOT_AFTER = "tokenNotAfter"; + public final static String TOKEN_SUBJECT = "tokenSubject"; + public final static String TOKEN_SPKI = "tokenSPKI"; + public final static String TOKEN_ALGORITHM = "tokenAlgorithm"; + public final static String TOKEN_PUBLIC_KEY = "tokenPublicKey"; + public final static String TOKEN_PUBLIC_KEY_MODULUS = "tokenPublicKeyModulus"; + public final static String TOKEN_PUBLIC_KEY_EXPONENT = "tokenPublicKeyExponent"; + public final static String TOKEN_EXTENSIONS = "tokenExtensions"; + public final static String TOKEN_SIGNATURE = "tokenSignature"; + + //extension pretty print + public final static String TOKEN_YES = "tokenYes"; + public final static String TOKEN_NO = "tokenNo"; + public final static String TOKEN_IDENTIFIER = "tokenIdentifier"; + public final static String TOKEN_CRITICAL = "tokenCritical"; + public final static String TOKEN_VALUE = "tokenValue"; + + //specific extension token + public final static String TOKEN_KEY_TYPE = "tokenKeyType"; + public final static String TOKEN_CERT_TYPE = "tokenCertType"; + public final static String TOKEN_SKI = "tokenSKI"; + public final static String TOKEN_AKI = "tokenAKI"; + public final static String TOKEN_ACCESS_DESC = "tokenAccessDesc"; + public final static String TOKEN_OCSP_NOCHECK = "tokenOcspNoCheck"; + public final static String TOKEN_EXTENDED_KEY_USAGE = "tokenExtendedKeyUsage"; + public final static String TOKEN_PRIVATE_KEY_USAGE = "tokenPrivateKeyUsage"; + public final static String TOKEN_PRESENCE_SERVER = "tokenPresenceServer"; + public final static String TOKEN_AIA = "tokenAIA"; + public final static String TOKEN_CERT_POLICIES = "tokenCertPolicies"; + public final static String TOKEN_SIA = "tokenSIA"; + public final static String TOKEN_KEY_USAGE = "tokenKeyUsage"; + public final static String TOKEN_CERT_USAGE = "tokenCertUsage"; + public final static String TOKEN_KEY_ID = "tokenKeyId"; + public final static String TOKEN_AUTH_NAME = "tokenAuthName"; + + public final static String TOKEN_CRL = "tokenCRL"; + public final static String TOKEN_THIS_UPDATE = "tokenThisUpdate"; + public final static String TOKEN_NEXT_UPDATE = "tokenNextUpdate"; + public final static String TOKEN_REVOKED_CERTIFICATES = "revokedCerts"; + public final static String TOKEN_REVOCATION_DATE = "revocationDate"; + + public final static String TOKEN_REVOCATION_REASON = "revocationReason"; + public final static String TOKEN_REASON = "reason"; + + public final static String TOKEN_BASIC_CONSTRAINTS = "basicConstraints"; + public final static String TOKEN_NAME_CONSTRAINTS = "tokenNameConstraints"; + public final static String TOKEN_NSC_COMMENT = "tokenNSCComment"; + public final static String TOKEN_IS_CA = "isCA"; + public final static String TOKEN_PATH_LEN = "pathLen"; + public final static String TOKEN_PATH_LEN_UNLIMITED = "pathLenUnlimited"; + public final static String TOKEN_PATH_LEN_UNDEFINED = "pathLenUndefined"; + public final static String TOKEN_PATH_LEN_INVALID = "pathLenInvalid"; + + public final static String TOKEN_CRL_NUMBER = "CRLNumber"; + public final static String TOKEN_NUMBER = "Number"; + + public final static String TOKEN_DELTA_CRL_INDICATOR = "DeltaCRLIndicator"; + public final static String TOKEN_BASE_CRL_NUMBER = "BaseCRLNumber"; + + public final static String TOKEN_CERT_SCOPE_OF_USE = "CertificateScopeOfUse"; + public final static String TOKEN_SCOPE_OF_USE = "ScopeOfUse"; + public final static String TOKEN_PORT = "Port"; + + public final static String TOKEN_ISSUER_ALT_NAME = "IssuerAlternativeName"; + public final static String TOKEN_ISSUER_NAMES = "IssuerNames"; + + public final static String TOKEN_SUBJECT_ALT_NAME = "SubjectAlternativeName"; + public final static String TOKEN_SUBJECT_NAME = "SubjectName"; + + public final static String TOKEN_DECODING_ERROR = "decodingError"; + + public final static String TOKEN_FRESHEST_CRL_EXT = "FreshestCRL"; + public final static String TOKEN_INHIBIT_ANY_POLICY_EXT = "InhibitAnyPolicy"; + public final static String TOKEN_SKIP_CERTS = "SkipCerts"; + + public final static String TOKEN_CRL_DP_EXT = "CRLDistributionPoints"; + public final static String TOKEN_CRLDP_NUMPOINTS = "CRLDP_NUMPOINTS"; + public final static String TOKEN_CRLDP_POINTN = "CRLDP_POINTN"; + public final static String TOKEN_CRLDP_DISTPOINT = "CRLDP_DISTPOINT"; + public final static String TOKEN_CRLDP_REASONS = "CRLDP_REASONS"; + public final static String TOKEN_CRLDP_CRLISSUER = "CRLDP_CRLISSUER"; + + public final static String TOKEN_ISSUING_DIST_POINT = "IssuingDistributionPoint"; + public final static String TOKEN_DIST_POINT_NAME = "DistributionPointName"; + public final static String TOKEN_FULL_NAME = "FullName"; + public final static String TOKEN_RELATIVE_NAME = "NameRelativeToCRLIssuer"; + public final static String TOKEN_ONLY_USER_CERTS = "OnlyContainsUserCerts"; + public final static String TOKEN_ONLY_CA_CERTS = "OnlyContainsCACerts"; + public final static String TOKEN_ONLY_SOME_REASONS = "OnlySomeReasons"; + public final static String TOKEN_INDIRECT_CRL = "IndirectCRL"; + + public final static String TOKEN_INVALIDITY_DATE = "invalidityDate"; + public final static String TOKEN_DATE_OF_INVALIDITY = "dateOfInvalidity"; + + public final static String TOKEN_CERTIFICATE_ISSUER = "CertificateIssuer"; + + public final static String TOKEN_HOLD_INSTRUCTION = "HoldInstruction"; + public final static String TOKEN_HOLD_INSTRUCTION_CODE = "HoldInstructionCode"; + public final static String TOKEN_POLICY_CONSTRAINTS = "PolicyConstraints"; + public final static String TOKEN_POLICY_MAPPINGS = "PolicyMappings"; + public final static String TOKEN_SUBJECT_DIR_ATTR = "SubjectDirectoryAttributes"; + + // policy constriants extension fields + public final static String TOKEN_INHIBIT_POLICY_MAPPING = "inhibitPolicyMapping"; + public final static String TOKEN_REQUIRE_EXPLICIT_POLICY = "requireExplicitPolicy"; + + // policy mappings extension fields + public final static String TOKEN_MAPPINGS = "mappings"; + public final static String TOKEN_MAP = "map"; + public final static String TOKEN_ISSUER_DOMAIN_POLICY = "issuerDomainPolicy"; + public final static String TOKEN_SUBJECT_DOMAIN_POLICY = "subjectDomainPolicy"; + + // subject directory attribute fields + public final static String TOKEN_ATTRIBUTES = "Attributes"; + public final static String TOKEN_ATTRIBUTE = "Attribute"; + public final static String TOKEN_VALUES = "Values"; + + // field values + public final static String TOKEN_NOT_SET = "notSet"; + public final static String TOKEN_NONE = "none"; + + public final static String TOKEN_CACHE_NOT_AVAILABLE = "cacheNotAvailable"; + + //Tokens should have blank_space as trailer + static final Object[][] contents = { + {TOKEN_CERTIFICATE, "Certificate: "}, + {TOKEN_DATA, "Data: "}, + {TOKEN_VERSION, "Version: "}, + {TOKEN_SERIAL, "Serial Number: "}, + {TOKEN_SIGALG, "Signature Algorithm: "}, + {TOKEN_ISSUER, "Issuer: "}, + {TOKEN_VALIDITY, "Validity: "}, + {TOKEN_NOT_BEFORE, "Not Before: "}, + {TOKEN_NOT_AFTER, "Not After: "}, + {TOKEN_SUBJECT, "Subject: "}, + {TOKEN_SPKI, "Subject Public Key Info: "}, + {TOKEN_ALGORITHM, "Algorithm: "}, + {TOKEN_PUBLIC_KEY, "Public Key: "}, + {TOKEN_PUBLIC_KEY_MODULUS, "Public Key Modulus: "}, + {TOKEN_PUBLIC_KEY_EXPONENT, "Exponent: "}, + {TOKEN_EXTENSIONS, "Extensions: "}, + {TOKEN_SIGNATURE, "Signature: "}, + {TOKEN_YES, "yes "}, + {TOKEN_NO, "no "}, + {TOKEN_IDENTIFIER, "Identifier: "}, + {TOKEN_CRITICAL, "Critical: "}, + {TOKEN_VALUE, "Value: "}, + {TOKEN_KEY_TYPE, "Key Type "}, + {TOKEN_CERT_TYPE, "Netscape Certificate Type "}, + {TOKEN_SKI, "Subject Key Identifier "}, + {TOKEN_AKI, "Authority Key Identifier "}, + {TOKEN_ACCESS_DESC, "Access Description: "}, + {TOKEN_OCSP_NOCHECK, "OCSP NoCheck: "}, + {TOKEN_EXTENDED_KEY_USAGE, "Extended Key Usage: "}, + {TOKEN_PRIVATE_KEY_USAGE, "Private Key Usage: "}, + {TOKEN_PRESENCE_SERVER, "Presence Server: "}, + {TOKEN_AIA, "Authority Info Access: "}, + {TOKEN_CERT_POLICIES, "Certificate Policies: "}, + {TOKEN_SIA, "Subject Info Access: "}, + {TOKEN_KEY_USAGE, "Key Usage: "}, + {KeyUsageExtension.DIGITAL_SIGNATURE, "Digital Signature "}, + {KeyUsageExtension.NON_REPUDIATION, "Non Repudiation "}, + {KeyUsageExtension.KEY_ENCIPHERMENT, "Key Encipherment "}, + {KeyUsageExtension.DATA_ENCIPHERMENT, "Data Encipherment "}, + {KeyUsageExtension.KEY_AGREEMENT, "Key Agreement "}, + {KeyUsageExtension.KEY_CERTSIGN, "Key CertSign "}, + {KeyUsageExtension.CRL_SIGN, "Crl Sign "}, + {KeyUsageExtension.ENCIPHER_ONLY, "Encipher Only "}, + {KeyUsageExtension.DECIPHER_ONLY, "Decipher Only "}, + {TOKEN_CERT_USAGE, "Certificate Usage: "}, + {NSCertTypeExtension.SSL_CLIENT, "SSL Client "}, + {NSCertTypeExtension.SSL_SERVER, "SSL Server "}, + {NSCertTypeExtension.EMAIL, "Secure Email "}, + {NSCertTypeExtension.OBJECT_SIGNING, "Object Signing "}, + {NSCertTypeExtension.SSL_CA, "SSL CA "}, + {NSCertTypeExtension.EMAIL_CA, "Secure Email CA "}, + {NSCertTypeExtension.OBJECT_SIGNING_CA, "ObjectSigning CA "}, + {TOKEN_KEY_ID, "Key Identifier: "}, + {TOKEN_AUTH_NAME, "Authority Name: "}, + {TOKEN_CRL, "Certificate Revocation List: "}, + {TOKEN_THIS_UPDATE, "This Update: "}, + {TOKEN_NEXT_UPDATE, "Next Update: "}, + {TOKEN_REVOKED_CERTIFICATES, "Revoked Certificates: "}, + {TOKEN_REVOCATION_DATE, "Revocation Date: "}, + {TOKEN_REVOCATION_REASON, "Revocation Reason "}, + {TOKEN_REASON, "Reason: "}, + {TOKEN_BASIC_CONSTRAINTS, "Basic Constraints "}, + {TOKEN_NAME_CONSTRAINTS, "Name Constraints "}, + {TOKEN_NSC_COMMENT, "Netscape Comment "}, + {TOKEN_IS_CA, "Is CA: "}, + {TOKEN_PATH_LEN, "Path Length Constraint: "}, + {TOKEN_PATH_LEN_UNLIMITED, "UNLIMITED"}, + {TOKEN_PATH_LEN_UNDEFINED, "UNDEFINED"}, + {TOKEN_PATH_LEN_INVALID, "INVALID"}, + {TOKEN_CRL_NUMBER, "CRL Number "}, + {TOKEN_NUMBER, "Number: "}, + {TOKEN_DELTA_CRL_INDICATOR, "Delta CRL Indicator "}, + {TOKEN_BASE_CRL_NUMBER, "Base CRL Number: "}, + {TOKEN_CERT_SCOPE_OF_USE, "Certificate Scope of Use "}, + {TOKEN_SCOPE_OF_USE, "Scope of Use: "}, + {TOKEN_PORT, "Port: "}, + {TOKEN_ISSUER_ALT_NAME, "Issuer Alternative Name "}, + {TOKEN_ISSUER_NAMES, "Issuer Names: "}, + {TOKEN_SUBJECT_ALT_NAME, "Subject Alternative Name "}, + {TOKEN_DECODING_ERROR, "Decoding Error"}, + {TOKEN_FRESHEST_CRL_EXT, "Freshest CRL "}, + {TOKEN_INHIBIT_ANY_POLICY_EXT, "Inhibit Any-Policy "}, + {TOKEN_SKIP_CERTS, "Skip Certs: "}, + {TOKEN_CRL_DP_EXT, "CRL Distribution Points "}, + {TOKEN_CRLDP_NUMPOINTS, "Number of Points: "}, + {TOKEN_CRLDP_POINTN, "Point "}, + {TOKEN_CRLDP_DISTPOINT, "Distribution Point: "}, + {TOKEN_CRLDP_REASONS, "Reason Flags: "}, + {TOKEN_CRLDP_CRLISSUER, "CRL Issuer: "}, + {TOKEN_ISSUING_DIST_POINT, "Issuing Distribution Point "}, + {TOKEN_DIST_POINT_NAME, "Distribution Point: "}, + {TOKEN_FULL_NAME, "Full Name: "}, + {TOKEN_RELATIVE_NAME, "Name Relative To CRL Issuer: "}, + {TOKEN_ONLY_USER_CERTS, "Only Contains User Certificates: "}, + {TOKEN_ONLY_CA_CERTS, "Only Contains CA Certificates: "}, + {TOKEN_ONLY_SOME_REASONS, "Only Some Reasons: "}, + {TOKEN_INDIRECT_CRL, "Indirect CRL: "}, + {TOKEN_INVALIDITY_DATE, "Invalidity Date "}, + {TOKEN_DATE_OF_INVALIDITY, "Invalidity Date: "}, + {TOKEN_CERTIFICATE_ISSUER, "Certificate Issuer "}, + {TOKEN_HOLD_INSTRUCTION, "Hold Instruction Code "}, + {TOKEN_HOLD_INSTRUCTION_CODE, "Hold Instruction Code: "}, + {TOKEN_POLICY_CONSTRAINTS, "Policy Constraints "}, + {TOKEN_INHIBIT_POLICY_MAPPING, "Inhibit Policy Mapping: "}, + {TOKEN_REQUIRE_EXPLICIT_POLICY, "Require Explicit Policy: "}, + {TOKEN_POLICY_MAPPINGS, "Policy Mappings "}, + {TOKEN_MAPPINGS, "Mappings: "}, + {TOKEN_MAP, "Map "}, + {TOKEN_ISSUER_DOMAIN_POLICY, "Issuer Domain Policy: "}, + {TOKEN_SUBJECT_DOMAIN_POLICY, "Subject Domain Policy: "}, + {TOKEN_SUBJECT_DIR_ATTR, "Subject Directory Attributes "}, + {TOKEN_ATTRIBUTES, "Attributes:" }, + {TOKEN_ATTRIBUTE, "Attribute "}, + {TOKEN_VALUES, "Values: "}, + {TOKEN_NOT_SET, "not set"}, + {TOKEN_NONE, "none"}, + {TOKEN_CACHE_NOT_AVAILABLE, "CRL cache is not available. "}, + }; + +} diff --git a/pki/base/util/src/netscape/security/util/PubKeyPrettyPrint.java b/pki/base/util/src/netscape/security/util/PubKeyPrettyPrint.java new file mode 100644 index 000000000..645e7a371 --- /dev/null +++ b/pki/base/util/src/netscape/security/util/PubKeyPrettyPrint.java @@ -0,0 +1,126 @@ +// --- 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.util; + + +import java.io.*; +import java.util.*; +import java.text.*; +import java.security.cert.*; +import netscape.security.util.*; +import netscape.security.x509.*; +import netscape.security.provider.RSAPublicKey; +import java.security.*; + + +/** + * This class will display the certificate content in predefined + * format. + * + * @author Jack Pan-Chen + * @author Andrew Wnuk + * @version $Revision: 14564 $, $Date: 2007-05-01 10:40:13 -0700 (Tue, 01 May 2007) $ + */ +public class PubKeyPrettyPrint { + + /*========================================================== + * variables + *==========================================================*/ + private X509Key mX509Key = null; + private PrettyPrintFormat pp = null; + + /*========================================================== + * constructors + *==========================================================*/ + + public PubKeyPrettyPrint(PublicKey key) { + if (key instanceof X509Key) + mX509Key = (X509Key) key; + + pp = new PrettyPrintFormat(":"); + } + + /*========================================================== + * public methods + *==========================================================*/ + + /** + * This method return string representation of the certificate + * in predefined format using specified client local. I18N Support. + * + * @param clientLocale Locale to be used for localization + * @return string representation of the certificate + */ + public String toString(Locale clientLocale, int indentSize, int lineLen) { + + if (mX509Key != null) + return X509toString(clientLocale, indentSize, lineLen); + else + return null; + } + + public String X509toString(Locale clientLocale, int indentSize, int lineLen) { + + + //get I18N resources + ResourceBundle resource = ResourceBundle.getBundle( + PrettyPrintResources.class.getName()); + + StringBuffer sb = new StringBuffer(); + + try { + String alg = mX509Key.getAlgorithm(); + + //XXX I18N Algorithm Name ? + sb.append(pp.indent(indentSize) + resource.getString( + PrettyPrintResources.TOKEN_ALGORITHM) + + alg + " - " + + mX509Key.getAlgorithmId().getOID().toString() + "\n"); + + if (alg.equals("RSA")) { + + RSAPublicKey rsakey = new RSAPublicKey(mX509Key.getEncoded()); + + sb.append(pp.indent(indentSize) + resource.getString( + PrettyPrintResources.TOKEN_PUBLIC_KEY) + "\n"); + sb.append(pp.indent(indentSize + 4) + resource.getString( + PrettyPrintResources.TOKEN_PUBLIC_KEY_EXPONENT) + + rsakey.getPublicExponent().toInt() + "\n"); + sb.append(pp.indent(indentSize + 4) + resource.getString( + PrettyPrintResources.TOKEN_PUBLIC_KEY_MODULUS) + + "(" + rsakey.getKeySize() + " bits) :\n"); + sb.append(pp.toHexString( + rsakey.getModulus().toByteArray(), + indentSize + 8, lineLen)); + } else { + + // DSAPublicKey is more complicated to decode, since + // the DSAParams (PQG) is not fully decoded. + // So, we just print the entire public key blob + + sb.append(pp.indent(indentSize) + resource.getString( + PrettyPrintResources.TOKEN_PUBLIC_KEY) + "\n"); + sb.append(pp.toHexString(mX509Key.getKey(), indentSize + 4, lineLen)); + } + + } catch (Exception e) { + } + + return sb.toString(); + } +} -- cgit