summaryrefslogtreecommitdiffstats
path: root/base/util/src/netscape/security/util
diff options
context:
space:
mode:
Diffstat (limited to 'base/util/src/netscape/security/util')
-rw-r--r--base/util/src/netscape/security/util/ASN1CharStrConvMap.java168
-rw-r--r--base/util/src/netscape/security/util/ASN1CharsetProvider.java30
-rw-r--r--base/util/src/netscape/security/util/BigInt.java210
-rw-r--r--base/util/src/netscape/security/util/BitArray.java257
-rw-r--r--base/util/src/netscape/security/util/ByteArrayLexOrder.java58
-rw-r--r--base/util/src/netscape/security/util/ByteArrayTagOrder.java44
-rw-r--r--base/util/src/netscape/security/util/CertPrettyPrint.java345
-rw-r--r--base/util/src/netscape/security/util/CrlPrettyPrint.java271
-rw-r--r--base/util/src/netscape/security/util/DerEncoder.java40
-rw-r--r--base/util/src/netscape/security/util/DerInputBuffer.java186
-rw-r--r--base/util/src/netscape/security/util/DerInputStream.java662
-rw-r--r--base/util/src/netscape/security/util/DerOutputStream.java729
-rw-r--r--base/util/src/netscape/security/util/DerValue.java715
-rw-r--r--base/util/src/netscape/security/util/ExtPrettyPrint.java1653
-rw-r--r--base/util/src/netscape/security/util/IA5Charset.java24
-rw-r--r--base/util/src/netscape/security/util/IA5CharsetDecoder.java62
-rw-r--r--base/util/src/netscape/security/util/IA5CharsetEncoder.java69
-rw-r--r--base/util/src/netscape/security/util/ObjectIdentifier.java426
-rw-r--r--base/util/src/netscape/security/util/PrettyPrintFormat.java160
-rw-r--r--base/util/src/netscape/security/util/PrettyPrintResources.java301
-rw-r--r--base/util/src/netscape/security/util/PrintableCharset.java46
-rw-r--r--base/util/src/netscape/security/util/PrintableCharsetDecoder.java69
-rw-r--r--base/util/src/netscape/security/util/PrintableCharsetEncoder.java71
-rw-r--r--base/util/src/netscape/security/util/PubKeyPrettyPrint.java121
-rw-r--r--base/util/src/netscape/security/util/UniversalCharset.java24
-rw-r--r--base/util/src/netscape/security/util/UniversalCharsetDecoder.java98
-rw-r--r--base/util/src/netscape/security/util/UniversalCharsetEncoder.java68
27 files changed, 6907 insertions, 0 deletions
diff --git a/base/util/src/netscape/security/util/ASN1CharStrConvMap.java b/base/util/src/netscape/security/util/ASN1CharStrConvMap.java
new file mode 100644
index 000000000..c9c364f4f
--- /dev/null
+++ b/base/util/src/netscape/security/util/ASN1CharStrConvMap.java
@@ -0,0 +1,168 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package netscape.security.util;
+
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CharsetEncoder;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Maps a ASN.1 character string type to a charset encoder and decoder.
+ * The converter is used to convert a DerValue of a ASN.1 character string type
+ * from bytes to unicode characters and vice versa.
+ *
+ * <p>
+ * 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 an encoder for the specified DER tag.
+ *
+ * @param tag A DER tag of a ASN.1 character string type,
+ * for example DerValue.tag_PrintableString.
+ *
+ * @return An encoder for the DER tag.
+ */
+ public CharsetEncoder getEncoder(byte tag) {
+ Charset charset = charsets.get(tag);
+ if (charset == null)
+ return null;
+ return charset.newEncoder();
+ }
+
+ /**
+ * Get a decoder for the given DER tag.
+ *
+ * @param tag A DER tag of a ASN.1 character string type,
+ * for example DerValue.tag_PrintableString.
+ *
+ * @return A decoder for the DER tag.
+ */
+ public CharsetDecoder getDecoder(byte tag) {
+ Charset charset = charsets.get(tag);
+ if (charset == null)
+ return null;
+ return charset.newDecoder();
+ }
+
+ /**
+ * Add a tag-charset entry in the map.
+ *
+ * @param tag A DER tag of a ASN.1 character string type,
+ * ex. DerValue.tag_IA5String
+ * @param charset A charset for the tag.
+ */
+ public void addEntry(byte tag, Charset charset) {
+
+ Charset currentCharset = charsets.get(tag);
+
+ if (currentCharset != null) {
+ if (currentCharset != charset) {
+ throw new IllegalArgumentException(
+ "a DER tag to converter entry already exists.");
+ } else {
+ return;
+ }
+ }
+
+ charsets.put(tag, charset);
+ }
+
+ /**
+ * Get an iterator of all tags in the map.
+ *
+ * @return An Iterator of DER tags in the map as Bytes.
+ */
+ public Iterator<Byte> getTags() {
+ return charsets.keySet().iterator();
+ }
+
+ // 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 Map<Byte, Charset> charsets = new HashMap<Byte, Charset>();
+
+ private static ASN1CharStrConvMap defaultMap;
+
+ /**
+ * Create the default converter map on initialization
+ */
+ static {
+ ASN1CharsetProvider provider = new ASN1CharsetProvider();
+
+ defaultMap = new ASN1CharStrConvMap();
+ defaultMap.addEntry(DerValue.tag_PrintableString,
+ provider.charsetForName("ASN.1-Printable"));
+ defaultMap.addEntry(DerValue.tag_VisibleString,
+ provider.charsetForName("ASN.1-Printable"));
+ defaultMap.addEntry(DerValue.tag_IA5String,
+ provider.charsetForName("ASN.1-IA5"));
+ defaultMap.addEntry(DerValue.tag_BMPString,
+ Charset.forName("UnicodeBig"));
+ defaultMap.addEntry(DerValue.tag_UniversalString,
+ provider.charsetForName("ASN.1-Universal"));
+ // XXX this is an oversimplified implementation of T.61 strings, it
+ // doesn't handle all cases
+ defaultMap.addEntry(DerValue.tag_T61String,
+ Charset.forName("ISO-8859-1"));
+ // UTF8String added to ASN.1 in 1998
+ defaultMap.addEntry(DerValue.tag_UTF8String,
+ Charset.forName("UTF-8"));
+ defaultMap.addEntry(DerValue.tag_GeneralString,
+ Charset.forName("UTF-8"));
+ };
+
+};
diff --git a/base/util/src/netscape/security/util/ASN1CharsetProvider.java b/base/util/src/netscape/security/util/ASN1CharsetProvider.java
new file mode 100644
index 000000000..1de1c3c48
--- /dev/null
+++ b/base/util/src/netscape/security/util/ASN1CharsetProvider.java
@@ -0,0 +1,30 @@
+package netscape.security.util;
+
+import java.nio.charset.Charset;
+import java.nio.charset.spi.CharsetProvider;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+public class ASN1CharsetProvider extends CharsetProvider {
+
+ protected Map<String, Charset> charsets = new HashMap<String, Charset>();
+
+ public ASN1CharsetProvider() {
+ addCharset(new PrintableCharset());
+ addCharset(new IA5Charset());
+ addCharset(new UniversalCharset());
+ }
+
+ public Iterator<Charset> charsets() {
+ return charsets.values().iterator();
+ }
+
+ public Charset charsetForName(String charsetName) {
+ return charsets.get(charsetName);
+ }
+
+ public void addCharset(Charset cs) {
+ charsets.put(cs.name(), cs);
+ }
+}
diff --git a/base/util/src/netscape/security/util/BigInt.java b/base/util/src/netscape/security/util/BigInt.java
new file mode 100644
index 000000000..9210648f1
--- /dev/null
+++ b/base/util/src/netscape/security/util/BigInt.java
@@ -0,0 +1,210 @@
+// --- 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 <em>unsigned</em> 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.
+ *
+ * <P>
+ * <em><b>NOTE:</b> 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 = 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 <code>java.math.BigInteger</code>).
+ */
+ public byte[] toByteArray() {
+ if (places.length == 0) {
+ byte zero[] = new byte[1];
+ zero[0] = (byte) 0;
+ return zero;
+ } else {
+ return 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/base/util/src/netscape/security/util/BitArray.java b/base/util/src/netscape/security/util/BitArray.java
new file mode 100644
index 000000000..ab77c226e
--- /dev/null
+++ b/base/util/src/netscape/security/util/BitArray.java
@@ -0,0 +1,257 @@
+// --- 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/base/util/src/netscape/security/util/ByteArrayLexOrder.java b/base/util/src/netscape/security/util/ByteArrayLexOrder.java
new file mode 100644
index 000000000..2ee2f740e
--- /dev/null
+++ b/base/util/src/netscape/security/util/ByteArrayLexOrder.java
@@ -0,0 +1,58 @@
+// --- 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<byte[]> {
+
+ /**
+ * 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 <code>ClassCastException</code> if either argument is not a byte array.
+ */
+ public final int compare(byte[] bytes1, byte[] bytes2) {
+
+ 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/base/util/src/netscape/security/util/ByteArrayTagOrder.java b/base/util/src/netscape/security/util/ByteArrayTagOrder.java
new file mode 100644
index 000000000..e57a3b5f1
--- /dev/null
+++ b/base/util/src/netscape/security/util/ByteArrayTagOrder.java
@@ -0,0 +1,44 @@
+// --- 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<byte[]> {
+
+ /**
+ * 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 <code>ClassCastException</code> if either argument is not a byte array.
+ */
+
+ public final int compare(byte[] bytes1, byte[] bytes2) {
+
+ // 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/base/util/src/netscape/security/util/CertPrettyPrint.java b/base/util/src/netscape/security/util/CertPrettyPrint.java
new file mode 100644
index 000000000..3a8c65fd0
--- /dev/null
+++ b/base/util/src/netscape/security/util/CertPrettyPrint.java
@@ -0,0 +1,345 @@
+// --- 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.security.MessageDigest;
+import java.security.PublicKey;
+import java.security.cert.Certificate;
+import java.text.DateFormat;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.util.TimeZone;
+
+import netscape.security.x509.CertificateExtensions;
+import netscape.security.x509.CertificateX509Key;
+import netscape.security.x509.Extension;
+import netscape.security.x509.X509CertImpl;
+import netscape.security.x509.X509CertInfo;
+import netscape.security.x509.X509Key;
+
+import org.mozilla.jss.asn1.ASN1Util;
+import org.mozilla.jss.asn1.SET;
+import org.mozilla.jss.pkcs7.ContentInfo;
+import org.mozilla.jss.pkcs7.SignedData;
+
+/**
+ * This class will display the certificate content in predefined
+ * format.
+ *
+ * @author Jack Pan-Chen
+ * @version $Revision$, $Date$
+ */
+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/base/util/src/netscape/security/util/CrlPrettyPrint.java b/base/util/src/netscape/security/util/CrlPrettyPrint.java
new file mode 100644
index 000000000..edf1217ea
--- /dev/null
+++ b/base/util/src/netscape/security/util/CrlPrettyPrint.java
@@ -0,0 +1,271 @@
+// --- 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.text.DateFormat;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.util.Set;
+import java.util.TimeZone;
+
+import netscape.security.x509.CRLExtensions;
+import netscape.security.x509.Extension;
+import netscape.security.x509.RevokedCertificate;
+import netscape.security.x509.X509CRLImpl;
+
+/**
+ * This class will display the certificate content in predefined
+ * format.
+ *
+ * @author Andrew Wnuk
+ * @version $Revision$, $Date$
+ */
+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<RevokedCertificate> revokedCerts = mCRL.getRevokedCertificates();
+
+ if (revokedCerts != null) {
+ Iterator<RevokedCertificate> i = revokedCerts.iterator();
+ long l = 1;
+
+ while ((i.hasNext()) && ((crlSize == 0) || (pageStart + pageSize > l))) {
+ RevokedCertificate revokedCert = 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/base/util/src/netscape/security/util/DerEncoder.java b/base/util/src/netscape/security/util/DerEncoder.java
new file mode 100644
index 000000000..c2eb64fc0
--- /dev/null
+++ b/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/base/util/src/netscape/security/util/DerInputBuffer.java b/base/util/src/netscape/security/util/DerInputBuffer.java
new file mode 100644
index 000000000..7534f3d06
--- /dev/null
+++ b/base/util/src/netscape/security/util/DerInputBuffer.java
@@ -0,0 +1,186 @@
+// --- 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/base/util/src/netscape/security/util/DerInputStream.java b/base/util/src/netscape/security/util/DerInputStream.java
new file mode 100644
index 000000000..20ced6757
--- /dev/null
+++ b/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.IOException;
+import java.io.InputStream;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+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).
+ *
+ * <P>
+ * 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.
+ *
+ * <P>
+ * 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 <em>data</em> 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);
+ }
+
+ public void skipSequence(int startLen) throws IOException {
+ int b = buffer.read();
+ if (b != DerValue.tag_Sequence)
+ throw new IOException("Sequence tag error " + b);
+ int len = getLength(buffer);
+ buffer.skip(len);
+ }
+
+ /**
+ * 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<DerValue> vec = new Vector<DerValue>(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 <code>reset</code> will return here.
+ */
+ public void mark(int value) {
+ buffer.mark(value);
+ }
+
+ /**
+ * Return to the position of the last <code>mark</code> 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/base/util/src/netscape/security/util/DerOutputStream.java b/base/util/src/netscape/security/util/DerOutputStream.java
new file mode 100644
index 000000000..62290d604
--- /dev/null
+++ b/base/util/src/netscape/security/util/DerOutputStream.java
@@ -0,0 +1,729 @@
+// --- 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;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.CharsetEncoder;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
+
+/**
+ * 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.
+ *
+ * <P>
+ * 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 <em>DerValue.tag_Sequence</em>
+ * @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 <em>DerValue.tag_Sequence</em>
+ * @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
+ *
+ * <pre>
+ * <em> <field> [N] IMPLICIT <type></em>
+ * </pre>
+ *
+ * For example, <em>FooLength [1] IMPLICIT INTEGER</em>, 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<byte[]> 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 {
+ try {
+ CharsetEncoder encoder = ASN1CharStrConvMap.getDefault().getEncoder(tag);
+ if (encoder == null)
+ throw new IOException("No encoder for tag");
+
+ CharBuffer charBuffer = CharBuffer.wrap(s.toCharArray());
+ ByteBuffer byteBuffer = encoder.encode(charBuffer);
+
+ write(tag);
+ putLength(byteBuffer.limit());
+ write(byteBuffer.array(), byteBuffer.arrayOffset(), byteBuffer.limit());
+
+ } catch (CharacterCodingException e) {
+ throw new IOException("Not a valid string type " + tag, e);
+ }
+ }
+
+ 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.
+ *
+ * <P>
+ * 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.
+ *
+ * <P>
+ * 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 <code>DerOutputStream</code> to an <code>OutputStream</code>.
+ *
+ * @exception IOException on output error.
+ */
+ public void derEncode(OutputStream out) throws IOException {
+ out.write(toByteArray());
+ }
+}
diff --git a/base/util/src/netscape/security/util/DerValue.java b/base/util/src/netscape/security/util/DerValue.java
new file mode 100644
index 000000000..71b6f7f2c
--- /dev/null
+++ b/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.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.CharsetDecoder;
+import java.util.Arrays;
+
+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.
+ *
+ * <P>
+ * All DER-encoded data are triples <em>{type, length, data}</em>. This class represents such tagged values as they have
+ * been read (or constructed), and provides structured access to the encoded data.
+ *
+ * <P>
+ * 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(data.clone());
+ length = data.length;
+ this.data = new DerInputStream(buffer);
+ this.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
+ * @param offset offset of the data
+ * @param length length of the data
+ */
+ public DerValue(byte tag, byte[] data, int offset, int length) {
+ this(tag, Arrays.copyOfRange(data, offset, offset + length));
+ }
+
+ /*
+ * 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();
+ }
+
+ /*
+ * @eturns a string if the DerValue is a ASN.1 character string type and
+ * if there is a decoder for the type. Returns null otherwise.
+ */
+ public String getASN1CharString() throws IOException {
+ try {
+ CharsetDecoder decoder = ASN1CharStrConvMap.getDefault().getDecoder(tag);
+ if (decoder == null)
+ return null;
+
+ ByteBuffer byteBuffer = ByteBuffer.allocate(length);
+
+ data.reset();
+ data.getBytes(byteBuffer.array());
+
+ CharBuffer charBuffer = decoder.decode(byteBuffer);
+ return charBuffer.toString();
+
+ } catch (CharacterCodingException e) {
+ throw new IOException("Misformed DER value", e);
+ }
+ }
+
+ /**
+ * 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/base/util/src/netscape/security/util/ExtPrettyPrint.java b/base/util/src/netscape/security/util/ExtPrettyPrint.java
new file mode 100644
index 000000000..90d0d094f
--- /dev/null
+++ b/base/util/src/netscape/security/util/ExtPrettyPrint.java
@@ -0,0 +1,1653 @@
+// --- 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;
+import java.text.DateFormat;
+import java.util.Enumeration;
+import java.util.ResourceBundle;
+import java.util.Vector;
+
+import netscape.security.extensions.AccessDescription;
+import netscape.security.extensions.AuthInfoAccessExtension;
+import netscape.security.extensions.CertificateScopeEntry;
+import netscape.security.extensions.CertificateScopeOfUseExtension;
+import netscape.security.extensions.ExtendedKeyUsageExtension;
+import netscape.security.extensions.InhibitAnyPolicyExtension;
+import netscape.security.extensions.NSCertTypeExtension;
+import netscape.security.extensions.OCSPNoCheckExtension;
+import netscape.security.extensions.PresenceServerExtension;
+import netscape.security.extensions.SubjectInfoAccessExtension;
+import netscape.security.x509.Attribute;
+import netscape.security.x509.AuthorityKeyIdentifierExtension;
+import netscape.security.x509.BasicConstraintsExtension;
+import netscape.security.x509.CPSuri;
+import netscape.security.x509.CRLDistributionPoint;
+import netscape.security.x509.CRLDistributionPointsExtension;
+import netscape.security.x509.CRLDistributionPointsExtension.Reason;
+import netscape.security.x509.CRLNumberExtension;
+import netscape.security.x509.CRLReasonExtension;
+import netscape.security.x509.CertificateIssuerExtension;
+import netscape.security.x509.CertificatePoliciesExtension;
+import netscape.security.x509.CertificatePolicyInfo;
+import netscape.security.x509.CertificatePolicyMap;
+import netscape.security.x509.DeltaCRLIndicatorExtension;
+import netscape.security.x509.DisplayText;
+import netscape.security.x509.Extension;
+import netscape.security.x509.FreshestCRLExtension;
+import netscape.security.x509.GeneralName;
+import netscape.security.x509.GeneralNameInterface;
+import netscape.security.x509.GeneralNames;
+import netscape.security.x509.HoldInstructionExtension;
+import netscape.security.x509.InvalidityDateExtension;
+import netscape.security.x509.IssuerAlternativeNameExtension;
+import netscape.security.x509.IssuingDistributionPoint;
+import netscape.security.x509.IssuingDistributionPointExtension;
+import netscape.security.x509.KeyIdentifier;
+import netscape.security.x509.KeyUsageExtension;
+import netscape.security.x509.NSCCommentExtension;
+import netscape.security.x509.NameConstraintsExtension;
+import netscape.security.x509.NoticeReference;
+import netscape.security.x509.OIDMap;
+import netscape.security.x509.PolicyConstraintsExtension;
+import netscape.security.x509.PolicyMappingsExtension;
+import netscape.security.x509.PolicyQualifierInfo;
+import netscape.security.x509.PolicyQualifiers;
+import netscape.security.x509.PrivateKeyUsageExtension;
+import netscape.security.x509.Qualifier;
+import netscape.security.x509.RDN;
+import netscape.security.x509.SerialNumber;
+import netscape.security.x509.SubjectAlternativeNameExtension;
+import netscape.security.x509.SubjectDirAttributesExtension;
+import netscape.security.x509.SubjectKeyIdentifierExtension;
+import netscape.security.x509.UserNotice;
+
+/**
+ * This class will display the certificate content in predefined
+ * format.
+ *
+ * @author Andrew Wnuk
+ * @version $Revision$, $Date$
+ */
+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<ObjectIdentifier> 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 (mExt.isCritical()) {
+ sb.append(mResource.getString(PrettyPrintResources.TOKEN_YES) + "\n");
+ } else {
+ sb.append(mResource.getString(PrettyPrintResources.TOKEN_NO) + "\n");
+ }
+ Vector<CertificateScopeEntry> 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) + "<i>empty</i>\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) + "<i>empty</i>\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<CertificatePolicyMap> 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<Attribute> 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<String> 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;
+ @SuppressWarnings("unchecked")
+ Vector<CertificatePolicyInfo> cpv = (Vector<CertificatePolicyInfo>) cp.get("infos");
+ Enumeration<CertificatePolicyInfo> e = cpv.elements();
+
+ if (e != null) {
+ while (e.hasMoreElements()) {
+ CertificatePolicyInfo cpi = e.nextElement();
+
+ sb.append(pp.indent(mIndentSize + 8)
+ + "Policy Identifier: " + cpi.getPolicyIdentifier().getIdentifier().toString() + "\n");
+ PolicyQualifiers cpq = cpi.getPolicyQualifiers();
+ if (cpq != null) {
+ for (int i = 0; i < cpq.size(); i++) {
+ PolicyQualifierInfo pq = cpq.getInfoAt(i);
+ Qualifier q = pq.getQualifier();
+ if (q instanceof CPSuri) {
+ sb.append(pp.indent(mIndentSize + 12)
+ + "Policy Qualifier Identifier: CPS Pointer Qualifier - "
+ + pq.getId() + "\n");
+ sb.append(pp.indent(mIndentSize + 12)
+ + "Policy Qualifier Data: " + ((CPSuri) q).getURI() + "\n");
+ } else if (q instanceof UserNotice) {
+ sb.append(pp.indent(mIndentSize + 12)
+ + "Policy Qualifier Identifier: CPS User Notice Qualifier - "
+ + pq.getId() + "\n");
+ NoticeReference nref = ((UserNotice) q).getNoticeReference();
+ DisplayText dt = ((UserNotice) q).getDisplayText();
+ sb.append(pp.indent(mIndentSize + 12) + "Policy Qualifier Data: \n");
+ if (nref != null) {
+ sb.append(pp.indent(mIndentSize + 16)
+ + "Organization: " + nref.getOrganization().toString() + "\n");
+ sb.append(pp.indent(mIndentSize + 16) + "Notice Numbers: ");
+ int[] nums = nref.getNumbers();
+ for (int k = 0; k < nums.length; k++) {
+ if (k != 0) {
+ sb.append(",");
+ sb.append(nums[k]);
+ } else {
+ sb.append(nums[k]);
+ }
+ }
+ sb.append("\n");
+ }
+ if (dt != null) {
+ sb.append(pp.indent(mIndentSize + 16) + "Explicit Text: " + dt.toString() + "\n");
+ }
+ }
+ }
+ }
+ }
+ }
+ return sb.toString();
+ } catch (Exception e) {
+ return sb.toString();
+ }
+ }
+
+}
diff --git a/base/util/src/netscape/security/util/IA5Charset.java b/base/util/src/netscape/security/util/IA5Charset.java
new file mode 100644
index 000000000..db6da2331
--- /dev/null
+++ b/base/util/src/netscape/security/util/IA5Charset.java
@@ -0,0 +1,24 @@
+package netscape.security.util;
+
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CharsetEncoder;
+
+public class IA5Charset extends Charset {
+
+ public IA5Charset() {
+ super("ASN.1-IA5", null);
+ }
+
+ public boolean contains(Charset cs) {
+ return false;
+ }
+
+ public CharsetDecoder newDecoder() {
+ return new IA5CharsetDecoder(this);
+ }
+
+ public CharsetEncoder newEncoder() {
+ return new IA5CharsetEncoder(this);
+ }
+}
diff --git a/base/util/src/netscape/security/util/IA5CharsetDecoder.java b/base/util/src/netscape/security/util/IA5CharsetDecoder.java
new file mode 100644
index 000000000..620d65aca
--- /dev/null
+++ b/base/util/src/netscape/security/util/IA5CharsetDecoder.java
@@ -0,0 +1,62 @@
+// --- 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.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
+
+/**
+ * Converts bytes in ASN.1 IA5String character set to IA5String characters.
+ *
+ * @author Lily Hsiao
+ * @author Slava Galperin
+ */
+
+public class IA5CharsetDecoder extends CharsetDecoder {
+
+ public IA5CharsetDecoder(Charset cs) {
+ super(cs, 1, 1);
+ }
+
+ protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
+
+ while (true) {
+
+ if (in.remaining() < 1)
+ return CoderResult.UNDERFLOW;
+
+ in.mark();
+ byte b = in.get();
+
+ if (CodingErrorAction.REPORT == unmappableCharacterAction() && (b & 0x80) != 0) {
+ return CoderResult.unmappableForLength(1);
+ }
+
+ if (out.remaining() < 1) {
+ in.reset();
+ return CoderResult.OVERFLOW;
+ }
+
+ out.put((char) (b & 0x7f));
+ }
+ }
+}
diff --git a/base/util/src/netscape/security/util/IA5CharsetEncoder.java b/base/util/src/netscape/security/util/IA5CharsetEncoder.java
new file mode 100644
index 000000000..dad0c9a2d
--- /dev/null
+++ b/base/util/src/netscape/security/util/IA5CharsetEncoder.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 java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
+
+/**
+ * Converts characters in ASN.1 IA5String character set to IA5String bytes.
+ *
+ * @author Lily Hsiao
+ * @author Slava Galperin
+ */
+
+public class IA5CharsetEncoder extends CharsetEncoder {
+
+ public IA5CharsetEncoder(Charset cs) {
+ super(cs, 1, 1);
+ }
+
+ /*
+ * Converts an array of Unicode characters into an array of IA5String
+ * bytes and returns the conversion result.
+ * @param in input character buffer to convert.
+ * @param out byte buffer to store output.
+ * @return encoding result.
+ */
+ protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
+
+ while (true) {
+
+ if (in.remaining() < 1)
+ return CoderResult.UNDERFLOW;
+
+ in.mark();
+ char c = in.get();
+
+ if (CodingErrorAction.REPORT == unmappableCharacterAction() && (c & 0xFF80) != 0) {
+ return CoderResult.unmappableForLength(1);
+ }
+
+ if (out.remaining() < 1) {
+ in.reset();
+ return CoderResult.OVERFLOW;
+ }
+
+ out.put((byte) (c & 0x7f));
+ }
+ }
+}
diff --git a/base/util/src/netscape/security/util/ObjectIdentifier.java b/base/util/src/netscape/security/util/ObjectIdentifier.java
new file mode 100644
index 000000000..6ff02d1b0
--- /dev/null
+++ b/base/util/src/netscape/security/util/ObjectIdentifier.java
@@ -0,0 +1,426 @@
+// --- 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.Serializable;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+
+/**
+ * Represent an ISO Object Identifier.
+ *
+ * <P>
+ * 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.
+ *
+ * <P>
+ * 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 }.
+ *
+ * <P>
+ * <STRONG>NOTE:</STRONG> 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 <em>other</em> 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 < componentLen; i++) {
+ oflow = (h & 0xff800000) >> 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 Hashtable<String, ObjectIdentifier> mOIDs = new Hashtable<String, ObjectIdentifier>();
+
+ public static ObjectIdentifier getObjectIdentifier(String oid)
+ throws IOException {
+ int value;
+
+ if (oid == null)
+ throw new IOException("empty object identifier");
+
+ oid = oid.trim();
+
+ ObjectIdentifier thisOID = 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/base/util/src/netscape/security/util/PrettyPrintFormat.java b/base/util/src/netscape/security/util/PrettyPrintFormat.java
new file mode 100644
index 000000000..25bc23d26
--- /dev/null
+++ b/base/util/src/netscape/security/util/PrettyPrintFormat.java
@@ -0,0 +1,160 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package netscape.security.util;
+
+/**
+ * This class will display the certificate content in predefined
+ * format.
+ *
+ * @author Andrew Wnuk
+ * @version $Revision$, $Date$
+ */
+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/base/util/src/netscape/security/util/PrettyPrintResources.java b/base/util/src/netscape/security/util/PrettyPrintResources.java
new file mode 100644
index 000000000..a3f068f64
--- /dev/null
+++ b/base/util/src/netscape/security/util/PrettyPrintResources.java
@@ -0,0 +1,301 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package netscape.security.util;
+
+import java.util.ListResourceBundle;
+
+import netscape.security.extensions.NSCertTypeExtension;
+import netscape.security.x509.KeyUsageExtension;
+
+/**
+ * Resource Boundle for the Pretty Print
+ *
+ * @author Jack Pan-Chen
+ * @version $Revision$, $Date$
+ */
+
+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";
+ public final static String TOKEN_CACHE_IS_EMPTY = "cacheIsEmpty";
+
+ //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. " },
+ { TOKEN_CACHE_IS_EMPTY, "CRL cache is empty. " },
+ };
+
+}
diff --git a/base/util/src/netscape/security/util/PrintableCharset.java b/base/util/src/netscape/security/util/PrintableCharset.java
new file mode 100644
index 000000000..a9cf15c04
--- /dev/null
+++ b/base/util/src/netscape/security/util/PrintableCharset.java
@@ -0,0 +1,46 @@
+package netscape.security.util;
+
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CharsetEncoder;
+
+public class PrintableCharset extends Charset {
+
+ public PrintableCharset() {
+ super("ASN.1-Printable", null);
+ }
+
+ 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;
+ }
+ }
+
+ public boolean contains(Charset cs) {
+ return false;
+ }
+
+ public CharsetDecoder newDecoder() {
+ return new PrintableCharsetDecoder(this);
+ }
+
+ public CharsetEncoder newEncoder() {
+ return new PrintableCharsetEncoder(this);
+ }
+}
diff --git a/base/util/src/netscape/security/util/PrintableCharsetDecoder.java b/base/util/src/netscape/security/util/PrintableCharsetDecoder.java
new file mode 100644
index 000000000..014095494
--- /dev/null
+++ b/base/util/src/netscape/security/util/PrintableCharsetDecoder.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 java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
+
+/**
+ * Converts bytes in ASN.1 PrintableString character set to PrintableString
+ * characters.
+ *
+ * @author Lily Hsiao
+ * @author Slava Galperin
+ */
+
+public class PrintableCharsetDecoder extends CharsetDecoder {
+
+ public PrintableCharsetDecoder(Charset cs) {
+ super(cs, 1, 1);
+ }
+
+ protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
+
+ while (true) {
+
+ if (in.remaining() < 1)
+ return CoderResult.UNDERFLOW;
+
+ in.mark();
+ byte b = in.get();
+ char c = (char) (b & 0x7f);
+
+ if (CodingErrorAction.REPORT == unmappableCharacterAction() &&
+ !PrintableCharset.isPrintableChar(c)) {
+ /*
+ "bug" fix for 359010
+ return CoderResult.unmappableForLength(1);
+ */
+ continue;
+ }
+
+ if (out.remaining() < 1) {
+ in.reset();
+ return CoderResult.OVERFLOW;
+ }
+
+ out.put(c);
+ }
+ }
+}
diff --git a/base/util/src/netscape/security/util/PrintableCharsetEncoder.java b/base/util/src/netscape/security/util/PrintableCharsetEncoder.java
new file mode 100644
index 000000000..bc658096a
--- /dev/null
+++ b/base/util/src/netscape/security/util/PrintableCharsetEncoder.java
@@ -0,0 +1,71 @@
+// --- 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.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
+
+/**
+ * Converts characters in ASN.1 PrintableString character set to PrintableString
+ * bytes.
+ *
+ * @author Lily Hsiao
+ * @author Slava Galperin
+ */
+
+public class PrintableCharsetEncoder extends CharsetEncoder {
+
+ public PrintableCharsetEncoder(Charset cs) {
+ super(cs, 1, 1);
+ }
+
+ /*
+ * Converts an array of Unicode characters into an array of PrintableString
+ * bytes and returns the conversion result.
+ * @param in input character buffer to convert.
+ * @param out byte buffer to store output.
+ * @return encoding result.
+ */
+ protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
+
+ while (true) {
+
+ if (in.remaining() < 1)
+ return CoderResult.UNDERFLOW;
+
+ in.mark();
+ char c = in.get();
+
+ if (CodingErrorAction.REPORT == unmappableCharacterAction() &&
+ !PrintableCharset.isPrintableChar(c)) {
+ return CoderResult.unmappableForLength(1);
+ }
+
+ if (out.remaining() < 1) {
+ in.reset();
+ return CoderResult.OVERFLOW;
+ }
+
+ out.put((byte) (c & 0x7f));
+ }
+ }
+}
diff --git a/base/util/src/netscape/security/util/PubKeyPrettyPrint.java b/base/util/src/netscape/security/util/PubKeyPrettyPrint.java
new file mode 100644
index 000000000..46c007cd9
--- /dev/null
+++ b/base/util/src/netscape/security/util/PubKeyPrettyPrint.java
@@ -0,0 +1,121 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package netscape.security.util;
+
+import java.security.PublicKey;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+import netscape.security.provider.RSAPublicKey;
+import netscape.security.x509.X509Key;
+
+/**
+ * This class will display the certificate content in predefined
+ * format.
+ *
+ * @author Jack Pan-Chen
+ * @author Andrew Wnuk
+ * @version $Revision$, $Date$
+ */
+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();
+ }
+}
diff --git a/base/util/src/netscape/security/util/UniversalCharset.java b/base/util/src/netscape/security/util/UniversalCharset.java
new file mode 100644
index 000000000..59cc3c6f4
--- /dev/null
+++ b/base/util/src/netscape/security/util/UniversalCharset.java
@@ -0,0 +1,24 @@
+package netscape.security.util;
+
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CharsetEncoder;
+
+public class UniversalCharset extends Charset {
+
+ public UniversalCharset() {
+ super("ASN.1-Universal", null);
+ }
+
+ public boolean contains(Charset cs) {
+ return false;
+ }
+
+ public CharsetDecoder newDecoder() {
+ return new UniversalCharsetDecoder(this);
+ }
+
+ public CharsetEncoder newEncoder() {
+ return new UniversalCharsetEncoder(this);
+ }
+}
diff --git a/base/util/src/netscape/security/util/UniversalCharsetDecoder.java b/base/util/src/netscape/security/util/UniversalCharsetDecoder.java
new file mode 100644
index 000000000..a41c5ad50
--- /dev/null
+++ b/base/util/src/netscape/security/util/UniversalCharsetDecoder.java
@@ -0,0 +1,98 @@
+// --- 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.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
+
+/**
+ * Converts bytes in ASN.1 UniversalString character set to UniversalString
+ * characters.
+ *
+ * @author Lily Hsiao
+ * @author Slava Galperin
+ */
+
+public class UniversalCharsetDecoder extends CharsetDecoder {
+
+ public UniversalCharsetDecoder(Charset cs) {
+ super(cs, 0.25f, 1);
+ }
+
+ protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
+
+ while (true) {
+ // XXX we do not know what to do with truly UCS-4 characters here
+ // we also assumed network byte order
+
+ if (in.remaining() < 4)
+ return CoderResult.UNDERFLOW;
+
+ in.mark();
+ byte b0 = in.get();
+ byte b1 = in.get();
+ byte b2 = in.get();
+ byte b3 = in.get();
+
+ if (CodingErrorAction.REPORT == unmappableCharacterAction() &&
+ !((b0 == 0 && b1 == 0) || (b2 == 0 && b3 == 0))) {
+ return CoderResult.unmappableForLength(4);
+ }
+
+ char c;
+ if (b2 == 0 && b3 == 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
+ c = (char) (((b1 << 8) & 0xff00) + (b0 & 0x00ff));
+
+ } else { // (b0 == 0 && b1 == 0)
+ // 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
+
+ c = (char) (((b2 << 8) & 0xff00) + (b3 & 0x00ff));
+ }
+
+ if (out.remaining() < 1) {
+ in.reset();
+ return CoderResult.OVERFLOW;
+ }
+
+ out.put(c);
+ }
+ }
+}
diff --git a/base/util/src/netscape/security/util/UniversalCharsetEncoder.java b/base/util/src/netscape/security/util/UniversalCharsetEncoder.java
new file mode 100644
index 000000000..cd2a51296
--- /dev/null
+++ b/base/util/src/netscape/security/util/UniversalCharsetEncoder.java
@@ -0,0 +1,68 @@
+// --- 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.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CoderResult;
+
+/**
+ * Converts characters in ASN.1 UniversalString character set to UniversalString
+ * bytes.
+ *
+ * @author Lily Hsiao
+ * @author Slava Galperin
+ */
+
+public class UniversalCharsetEncoder extends CharsetEncoder {
+
+ public UniversalCharsetEncoder(Charset cs) {
+ super(cs, 4, 4, new byte[] { 0, 0, 0, 0 });
+ }
+
+ /*
+ * Converts an array of Unicode characters into an array of UniversalString
+ * bytes and returns the conversion result.
+ * @param in input character buffer to convert.
+ * @param out byte buffer to store output.
+ * @return encoding result.
+ */
+ protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
+
+ while (true) {
+
+ if (in.remaining() < 1)
+ return CoderResult.UNDERFLOW;
+
+ in.mark();
+ char c = in.get();
+
+ if (out.remaining() < 4) {
+ in.reset();
+ return CoderResult.OVERFLOW;
+ }
+
+ out.put((byte) 0);
+ out.put((byte) 0);
+ out.put((byte) ((c >> 8) & 0xff));
+ out.put((byte) (c & 0xff));
+ }
+ }
+}